1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2014 Linaro Ltd 4 * 5 * Author: Ulf Hansson <ulf.hansson@linaro.org> 6 * 7 * Simple MMC power sequence management 8 */ 9 #include <linux/clk.h> 10 #include <linux/init.h> 11 #include <linux/kernel.h> 12 #include <linux/platform_device.h> 13 #include <linux/module.h> 14 #include <linux/slab.h> 15 #include <linux/device.h> 16 #include <linux/err.h> 17 #include <linux/gpio/consumer.h> 18 #include <linux/delay.h> 19 #include <linux/property.h> 20 21 #include <linux/mmc/host.h> 22 23 #include "pwrseq.h" 24 25 struct mmc_pwrseq_simple { 26 struct mmc_pwrseq pwrseq; 27 bool clk_enabled; 28 u32 post_power_on_delay_ms; 29 u32 power_off_delay_us; 30 struct clk *ext_clk; 31 struct gpio_descs *reset_gpios; 32 }; 33 34 #define to_pwrseq_simple(p) container_of(p, struct mmc_pwrseq_simple, pwrseq) 35 36 static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq, 37 int value) 38 { 39 struct gpio_descs *reset_gpios = pwrseq->reset_gpios; 40 41 if (!IS_ERR(reset_gpios)) { 42 unsigned long *values; 43 int nvalues = reset_gpios->ndescs; 44 45 values = bitmap_alloc(nvalues, GFP_KERNEL); 46 if (!values) 47 return; 48 49 if (value) 50 bitmap_fill(values, nvalues); 51 else 52 bitmap_zero(values, nvalues); 53 54 gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, 55 reset_gpios->info, values); 56 57 kfree(values); 58 } 59 } 60 61 static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host) 62 { 63 struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq); 64 65 if (!IS_ERR(pwrseq->ext_clk) && !pwrseq->clk_enabled) { 66 clk_prepare_enable(pwrseq->ext_clk); 67 pwrseq->clk_enabled = true; 68 } 69 70 mmc_pwrseq_simple_set_gpios_value(pwrseq, 1); 71 } 72 73 static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host) 74 { 75 struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq); 76 77 mmc_pwrseq_simple_set_gpios_value(pwrseq, 0); 78 79 if (pwrseq->post_power_on_delay_ms) 80 msleep(pwrseq->post_power_on_delay_ms); 81 } 82 83 static void mmc_pwrseq_simple_power_off(struct mmc_host *host) 84 { 85 struct mmc_pwrseq_simple *pwrseq = to_pwrseq_simple(host->pwrseq); 86 87 mmc_pwrseq_simple_set_gpios_value(pwrseq, 1); 88 89 if (pwrseq->power_off_delay_us) 90 usleep_range(pwrseq->power_off_delay_us, 91 2 * pwrseq->power_off_delay_us); 92 93 if (!IS_ERR(pwrseq->ext_clk) && pwrseq->clk_enabled) { 94 clk_disable_unprepare(pwrseq->ext_clk); 95 pwrseq->clk_enabled = false; 96 } 97 } 98 99 static const struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = { 100 .pre_power_on = mmc_pwrseq_simple_pre_power_on, 101 .post_power_on = mmc_pwrseq_simple_post_power_on, 102 .power_off = mmc_pwrseq_simple_power_off, 103 }; 104 105 static const struct of_device_id mmc_pwrseq_simple_of_match[] = { 106 { .compatible = "mmc-pwrseq-simple",}, 107 {/* sentinel */}, 108 }; 109 MODULE_DEVICE_TABLE(of, mmc_pwrseq_simple_of_match); 110 111 static int mmc_pwrseq_simple_probe(struct platform_device *pdev) 112 { 113 struct mmc_pwrseq_simple *pwrseq; 114 struct device *dev = &pdev->dev; 115 116 pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL); 117 if (!pwrseq) 118 return -ENOMEM; 119 120 pwrseq->ext_clk = devm_clk_get(dev, "ext_clock"); 121 if (IS_ERR(pwrseq->ext_clk) && PTR_ERR(pwrseq->ext_clk) != -ENOENT) 122 return PTR_ERR(pwrseq->ext_clk); 123 124 pwrseq->reset_gpios = devm_gpiod_get_array(dev, "reset", 125 GPIOD_OUT_HIGH); 126 if (IS_ERR(pwrseq->reset_gpios) && 127 PTR_ERR(pwrseq->reset_gpios) != -ENOENT && 128 PTR_ERR(pwrseq->reset_gpios) != -ENOSYS) { 129 return PTR_ERR(pwrseq->reset_gpios); 130 } 131 132 device_property_read_u32(dev, "post-power-on-delay-ms", 133 &pwrseq->post_power_on_delay_ms); 134 device_property_read_u32(dev, "power-off-delay-us", 135 &pwrseq->power_off_delay_us); 136 137 pwrseq->pwrseq.dev = dev; 138 pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops; 139 pwrseq->pwrseq.owner = THIS_MODULE; 140 platform_set_drvdata(pdev, pwrseq); 141 142 return mmc_pwrseq_register(&pwrseq->pwrseq); 143 } 144 145 static int mmc_pwrseq_simple_remove(struct platform_device *pdev) 146 { 147 struct mmc_pwrseq_simple *pwrseq = platform_get_drvdata(pdev); 148 149 mmc_pwrseq_unregister(&pwrseq->pwrseq); 150 151 return 0; 152 } 153 154 static struct platform_driver mmc_pwrseq_simple_driver = { 155 .probe = mmc_pwrseq_simple_probe, 156 .remove = mmc_pwrseq_simple_remove, 157 .driver = { 158 .name = "pwrseq_simple", 159 .of_match_table = mmc_pwrseq_simple_of_match, 160 }, 161 }; 162 163 module_platform_driver(mmc_pwrseq_simple_driver); 164 MODULE_LICENSE("GPL v2"); 165