cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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");