Blame view

kernel/linux-rt-4.4.41/drivers/mmc/core/pwrseq_emmc.c 2.38 KB
5113f6f70   김현기   kernel add
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
  /*
   * Copyright (C) 2015, Samsung Electronics Co., Ltd.
   *
   * Author: Marek Szyprowski <m.szyprowski@samsung.com>
   *
   * License terms: GNU General Public License (GPL) version 2
   *
   * Simple eMMC hardware reset provider
   */
  #include <linux/delay.h>
  #include <linux/kernel.h>
  #include <linux/slab.h>
  #include <linux/device.h>
  #include <linux/err.h>
  #include <linux/gpio/consumer.h>
  #include <linux/reboot.h>
  
  #include <linux/mmc/host.h>
  
  #include "pwrseq.h"
  
  struct mmc_pwrseq_emmc {
  	struct mmc_pwrseq pwrseq;
  	struct notifier_block reset_nb;
  	struct gpio_desc *reset_gpio;
  };
  
  static void __mmc_pwrseq_emmc_reset(struct mmc_pwrseq_emmc *pwrseq)
  {
  	gpiod_set_value(pwrseq->reset_gpio, 1);
  	udelay(1);
  	gpiod_set_value(pwrseq->reset_gpio, 0);
  	udelay(200);
  }
  
  static void mmc_pwrseq_emmc_reset(struct mmc_host *host)
  {
  	struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq,
  					struct mmc_pwrseq_emmc, pwrseq);
  
  	__mmc_pwrseq_emmc_reset(pwrseq);
  }
  
  static void mmc_pwrseq_emmc_free(struct mmc_host *host)
  {
  	struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq,
  					struct mmc_pwrseq_emmc, pwrseq);
  
  	unregister_restart_handler(&pwrseq->reset_nb);
  	gpiod_put(pwrseq->reset_gpio);
  	kfree(pwrseq);
  }
  
  static struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = {
  	.post_power_on = mmc_pwrseq_emmc_reset,
  	.free = mmc_pwrseq_emmc_free,
  };
  
  static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this,
  				    unsigned long mode, void *cmd)
  {
  	struct mmc_pwrseq_emmc *pwrseq = container_of(this,
  					struct mmc_pwrseq_emmc, reset_nb);
  
  	__mmc_pwrseq_emmc_reset(pwrseq);
  	return NOTIFY_DONE;
  }
  
  struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host,
  					 struct device *dev)
  {
  	struct mmc_pwrseq_emmc *pwrseq;
  	int ret = 0;
  
  	pwrseq = kzalloc(sizeof(struct mmc_pwrseq_emmc), GFP_KERNEL);
  	if (!pwrseq)
  		return ERR_PTR(-ENOMEM);
  
  	pwrseq->reset_gpio = gpiod_get(dev, "reset", GPIOD_OUT_LOW);
  	if (IS_ERR(pwrseq->reset_gpio)) {
  		ret = PTR_ERR(pwrseq->reset_gpio);
  		goto free;
  	}
  
  	/*
  	 * register reset handler to ensure emmc reset also from
  	 * emergency_reboot(), priority 255 is the highest priority
  	 * so it will be executed before any system reboot handler.
  	 */
  	pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb;
  	pwrseq->reset_nb.priority = 255;
  	register_restart_handler(&pwrseq->reset_nb);
  
  	pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops;
  
  	return &pwrseq->pwrseq;
  free:
  	kfree(pwrseq);
  	return ERR_PTR(ret);
  }