pwrseq_simple.c (4249B)
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 25struct 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 36static 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 bitmap_free(values); 58 } 59} 60 61static 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 73static 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 83static 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 99static 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 105static const struct of_device_id mmc_pwrseq_simple_of_match[] = { 106 { .compatible = "mmc-pwrseq-simple",}, 107 {/* sentinel */}, 108}; 109MODULE_DEVICE_TABLE(of, mmc_pwrseq_simple_of_match); 110 111static 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 145static 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 154static 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 163module_platform_driver(mmc_pwrseq_simple_driver); 164MODULE_LICENSE("GPL v2");