summaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/of_mmc_spi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/of_mmc_spi.c')
-rw-r--r--drivers/mmc/host/of_mmc_spi.c97
1 files changed, 97 insertions, 0 deletions
diff --git a/drivers/mmc/host/of_mmc_spi.c b/drivers/mmc/host/of_mmc_spi.c
new file mode 100644
index 000000000..bf54776fb
--- /dev/null
+++ b/drivers/mmc/host/of_mmc_spi.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * OpenFirmware bindings for the MMC-over-SPI driver
+ *
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/mmc_spi.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/host.h>
+
+MODULE_LICENSE("GPL");
+
+struct of_mmc_spi {
+ struct mmc_spi_platform_data pdata;
+ int detect_irq;
+};
+
+static struct of_mmc_spi *to_of_mmc_spi(struct device *dev)
+{
+ return container_of(dev->platform_data, struct of_mmc_spi, pdata);
+}
+
+static int of_mmc_spi_init(struct device *dev,
+ irqreturn_t (*irqhandler)(int, void *), void *mmc)
+{
+ struct of_mmc_spi *oms = to_of_mmc_spi(dev);
+
+ return request_threaded_irq(oms->detect_irq, NULL, irqhandler,
+ IRQF_ONESHOT, dev_name(dev), mmc);
+}
+
+static void of_mmc_spi_exit(struct device *dev, void *mmc)
+{
+ struct of_mmc_spi *oms = to_of_mmc_spi(dev);
+
+ free_irq(oms->detect_irq, mmc);
+}
+
+struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi)
+{
+ struct mmc_host *mmc = dev_get_drvdata(&spi->dev);
+ struct device *dev = &spi->dev;
+ struct of_mmc_spi *oms;
+
+ if (dev->platform_data || !dev_fwnode(dev))
+ return dev->platform_data;
+
+ oms = kzalloc(sizeof(*oms), GFP_KERNEL);
+ if (!oms)
+ return NULL;
+
+ if (mmc_of_parse_voltage(mmc, &oms->pdata.ocr_mask) < 0)
+ goto err_ocr;
+
+ oms->detect_irq = spi->irq;
+ if (oms->detect_irq > 0) {
+ oms->pdata.init = of_mmc_spi_init;
+ oms->pdata.exit = of_mmc_spi_exit;
+ } else {
+ oms->pdata.caps |= MMC_CAP_NEEDS_POLL;
+ }
+ if (device_property_read_bool(dev, "cap-sd-highspeed"))
+ oms->pdata.caps |= MMC_CAP_SD_HIGHSPEED;
+ if (device_property_read_bool(dev, "cap-mmc-highspeed"))
+ oms->pdata.caps |= MMC_CAP_MMC_HIGHSPEED;
+
+ dev->platform_data = &oms->pdata;
+ return dev->platform_data;
+err_ocr:
+ kfree(oms);
+ return NULL;
+}
+EXPORT_SYMBOL(mmc_spi_get_pdata);
+
+void mmc_spi_put_pdata(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct of_mmc_spi *oms = to_of_mmc_spi(dev);
+
+ if (!dev->platform_data || !dev_fwnode(dev))
+ return;
+
+ kfree(oms);
+ dev->platform_data = NULL;
+}
+EXPORT_SYMBOL(mmc_spi_put_pdata);