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

slot-gpio.c (6291B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Generic GPIO card-detect helper
      4 *
      5 * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
      6 */
      7
      8#include <linux/err.h>
      9#include <linux/gpio/consumer.h>
     10#include <linux/interrupt.h>
     11#include <linux/jiffies.h>
     12#include <linux/mmc/host.h>
     13#include <linux/mmc/slot-gpio.h>
     14#include <linux/module.h>
     15#include <linux/slab.h>
     16
     17#include "slot-gpio.h"
     18
     19struct mmc_gpio {
     20	struct gpio_desc *ro_gpio;
     21	struct gpio_desc *cd_gpio;
     22	irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id);
     23	char *ro_label;
     24	char *cd_label;
     25	u32 cd_debounce_delay_ms;
     26};
     27
     28static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
     29{
     30	/* Schedule a card detection after a debounce timeout */
     31	struct mmc_host *host = dev_id;
     32	struct mmc_gpio *ctx = host->slot.handler_priv;
     33
     34	host->trigger_card_event = true;
     35	mmc_detect_change(host, msecs_to_jiffies(ctx->cd_debounce_delay_ms));
     36
     37	return IRQ_HANDLED;
     38}
     39
     40int mmc_gpio_alloc(struct mmc_host *host)
     41{
     42	const char *devname = dev_name(host->parent);
     43	struct mmc_gpio *ctx;
     44
     45	ctx = devm_kzalloc(host->parent, sizeof(*ctx), GFP_KERNEL);
     46	if (!ctx)
     47		return -ENOMEM;
     48
     49	ctx->cd_debounce_delay_ms = 200;
     50	ctx->cd_label = devm_kasprintf(host->parent, GFP_KERNEL, "%s cd", devname);
     51	if (!ctx->cd_label)
     52		return -ENOMEM;
     53	ctx->ro_label = devm_kasprintf(host->parent, GFP_KERNEL, "%s ro", devname);
     54	if (!ctx->ro_label)
     55		return -ENOMEM;
     56	host->slot.handler_priv = ctx;
     57	host->slot.cd_irq = -EINVAL;
     58
     59	return 0;
     60}
     61
     62int mmc_gpio_get_ro(struct mmc_host *host)
     63{
     64	struct mmc_gpio *ctx = host->slot.handler_priv;
     65
     66	if (!ctx || !ctx->ro_gpio)
     67		return -ENOSYS;
     68
     69	return gpiod_get_value_cansleep(ctx->ro_gpio);
     70}
     71EXPORT_SYMBOL(mmc_gpio_get_ro);
     72
     73int mmc_gpio_get_cd(struct mmc_host *host)
     74{
     75	struct mmc_gpio *ctx = host->slot.handler_priv;
     76	int cansleep;
     77
     78	if (!ctx || !ctx->cd_gpio)
     79		return -ENOSYS;
     80
     81	cansleep = gpiod_cansleep(ctx->cd_gpio);
     82	return cansleep ?
     83		gpiod_get_value_cansleep(ctx->cd_gpio) :
     84		gpiod_get_value(ctx->cd_gpio);
     85}
     86EXPORT_SYMBOL(mmc_gpio_get_cd);
     87
     88void mmc_gpiod_request_cd_irq(struct mmc_host *host)
     89{
     90	struct mmc_gpio *ctx = host->slot.handler_priv;
     91	int irq = -EINVAL;
     92	int ret;
     93
     94	if (host->slot.cd_irq >= 0 || !ctx || !ctx->cd_gpio)
     95		return;
     96
     97	/*
     98	 * Do not use IRQ if the platform prefers to poll, e.g., because that
     99	 * IRQ number is already used by another unit and cannot be shared.
    100	 */
    101	if (!(host->caps & MMC_CAP_NEEDS_POLL))
    102		irq = gpiod_to_irq(ctx->cd_gpio);
    103
    104	if (irq >= 0) {
    105		if (!ctx->cd_gpio_isr)
    106			ctx->cd_gpio_isr = mmc_gpio_cd_irqt;
    107		ret = devm_request_threaded_irq(host->parent, irq,
    108			NULL, ctx->cd_gpio_isr,
    109			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
    110			ctx->cd_label, host);
    111		if (ret < 0)
    112			irq = ret;
    113	}
    114
    115	host->slot.cd_irq = irq;
    116
    117	if (irq < 0)
    118		host->caps |= MMC_CAP_NEEDS_POLL;
    119}
    120EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
    121
    122int mmc_gpio_set_cd_wake(struct mmc_host *host, bool on)
    123{
    124	int ret = 0;
    125
    126	if (!(host->caps & MMC_CAP_CD_WAKE) ||
    127	    host->slot.cd_irq < 0 ||
    128	    on == host->slot.cd_wake_enabled)
    129		return 0;
    130
    131	if (on) {
    132		ret = enable_irq_wake(host->slot.cd_irq);
    133		host->slot.cd_wake_enabled = !ret;
    134	} else {
    135		disable_irq_wake(host->slot.cd_irq);
    136		host->slot.cd_wake_enabled = false;
    137	}
    138
    139	return ret;
    140}
    141EXPORT_SYMBOL(mmc_gpio_set_cd_wake);
    142
    143/* Register an alternate interrupt service routine for
    144 * the card-detect GPIO.
    145 */
    146void mmc_gpio_set_cd_isr(struct mmc_host *host,
    147			 irqreturn_t (*isr)(int irq, void *dev_id))
    148{
    149	struct mmc_gpio *ctx = host->slot.handler_priv;
    150
    151	WARN_ON(ctx->cd_gpio_isr);
    152	ctx->cd_gpio_isr = isr;
    153}
    154EXPORT_SYMBOL(mmc_gpio_set_cd_isr);
    155
    156/**
    157 * mmc_gpiod_request_cd - request a gpio descriptor for card-detection
    158 * @host: mmc host
    159 * @con_id: function within the GPIO consumer
    160 * @idx: index of the GPIO to obtain in the consumer
    161 * @override_active_level: ignore %GPIO_ACTIVE_LOW flag
    162 * @debounce: debounce time in microseconds
    163 *
    164 * Note that this must be called prior to mmc_add_host()
    165 * otherwise the caller must also call mmc_gpiod_request_cd_irq().
    166 *
    167 * Returns zero on success, else an error.
    168 */
    169int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
    170			 unsigned int idx, bool override_active_level,
    171			 unsigned int debounce)
    172{
    173	struct mmc_gpio *ctx = host->slot.handler_priv;
    174	struct gpio_desc *desc;
    175	int ret;
    176
    177	desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN);
    178	if (IS_ERR(desc))
    179		return PTR_ERR(desc);
    180
    181	/* Update default label if no con_id provided */
    182	if (!con_id)
    183		gpiod_set_consumer_name(desc, ctx->cd_label);
    184
    185	if (debounce) {
    186		ret = gpiod_set_debounce(desc, debounce);
    187		if (ret < 0)
    188			ctx->cd_debounce_delay_ms = debounce / 1000;
    189	}
    190
    191	/* override forces default (active-low) polarity ... */
    192	if (override_active_level && !gpiod_is_active_low(desc))
    193		gpiod_toggle_active_low(desc);
    194
    195	/* ... or active-high */
    196	if (host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH)
    197		gpiod_toggle_active_low(desc);
    198
    199	ctx->cd_gpio = desc;
    200
    201	return 0;
    202}
    203EXPORT_SYMBOL(mmc_gpiod_request_cd);
    204
    205bool mmc_can_gpio_cd(struct mmc_host *host)
    206{
    207	struct mmc_gpio *ctx = host->slot.handler_priv;
    208
    209	return ctx->cd_gpio ? true : false;
    210}
    211EXPORT_SYMBOL(mmc_can_gpio_cd);
    212
    213/**
    214 * mmc_gpiod_request_ro - request a gpio descriptor for write protection
    215 * @host: mmc host
    216 * @con_id: function within the GPIO consumer
    217 * @idx: index of the GPIO to obtain in the consumer
    218 * @debounce: debounce time in microseconds
    219 *
    220 * Returns zero on success, else an error.
    221 */
    222int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
    223			 unsigned int idx, unsigned int debounce)
    224{
    225	struct mmc_gpio *ctx = host->slot.handler_priv;
    226	struct gpio_desc *desc;
    227	int ret;
    228
    229	desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN);
    230	if (IS_ERR(desc))
    231		return PTR_ERR(desc);
    232
    233	/* Update default label if no con_id provided */
    234	if (!con_id)
    235		gpiod_set_consumer_name(desc, ctx->ro_label);
    236
    237	if (debounce) {
    238		ret = gpiod_set_debounce(desc, debounce);
    239		if (ret < 0)
    240			return ret;
    241	}
    242
    243	if (host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH)
    244		gpiod_toggle_active_low(desc);
    245
    246	ctx->ro_gpio = desc;
    247
    248	return 0;
    249}
    250EXPORT_SYMBOL(mmc_gpiod_request_ro);
    251
    252bool mmc_can_gpio_ro(struct mmc_host *host)
    253{
    254	struct mmc_gpio *ctx = host->slot.handler_priv;
    255
    256	return ctx->ro_gpio ? true : false;
    257}
    258EXPORT_SYMBOL(mmc_can_gpio_ro);