1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * OpenFirmware bindings for the MMC-over-SPI driver |
4 | * |
5 | * Copyright (c) MontaVista Software, Inc. 2008. |
6 | * |
7 | * Author: Anton Vorontsov <avorontsov@ru.mvista.com> |
8 | */ |
9 | |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> |
12 | #include <linux/device.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/irq.h> |
15 | #include <linux/of.h> |
16 | #include <linux/of_irq.h> |
17 | #include <linux/spi/spi.h> |
18 | #include <linux/spi/mmc_spi.h> |
19 | #include <linux/mmc/core.h> |
20 | #include <linux/mmc/host.h> |
21 | |
22 | MODULE_LICENSE("GPL" ); |
23 | |
24 | struct of_mmc_spi { |
25 | struct mmc_spi_platform_data pdata; |
26 | int detect_irq; |
27 | }; |
28 | |
29 | static struct of_mmc_spi *to_of_mmc_spi(struct device *dev) |
30 | { |
31 | return container_of(dev->platform_data, struct of_mmc_spi, pdata); |
32 | } |
33 | |
34 | static int of_mmc_spi_init(struct device *dev, |
35 | irqreturn_t (*irqhandler)(int, void *), void *mmc) |
36 | { |
37 | struct of_mmc_spi *oms = to_of_mmc_spi(dev); |
38 | |
39 | return request_threaded_irq(irq: oms->detect_irq, NULL, thread_fn: irqhandler, |
40 | IRQF_ONESHOT, name: dev_name(dev), dev: mmc); |
41 | } |
42 | |
43 | static void of_mmc_spi_exit(struct device *dev, void *mmc) |
44 | { |
45 | struct of_mmc_spi *oms = to_of_mmc_spi(dev); |
46 | |
47 | free_irq(oms->detect_irq, mmc); |
48 | } |
49 | |
50 | struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi) |
51 | { |
52 | struct mmc_host *mmc = dev_get_drvdata(dev: &spi->dev); |
53 | struct device *dev = &spi->dev; |
54 | struct of_mmc_spi *oms; |
55 | |
56 | if (dev->platform_data || !dev_fwnode(dev)) |
57 | return dev->platform_data; |
58 | |
59 | oms = kzalloc(size: sizeof(*oms), GFP_KERNEL); |
60 | if (!oms) |
61 | return NULL; |
62 | |
63 | if (mmc_of_parse_voltage(host: mmc, mask: &oms->pdata.ocr_mask) < 0) |
64 | goto err_ocr; |
65 | |
66 | oms->detect_irq = spi->irq; |
67 | if (oms->detect_irq > 0) { |
68 | oms->pdata.init = of_mmc_spi_init; |
69 | oms->pdata.exit = of_mmc_spi_exit; |
70 | } else { |
71 | oms->pdata.caps |= MMC_CAP_NEEDS_POLL; |
72 | } |
73 | if (device_property_read_bool(dev, propname: "cap-sd-highspeed" )) |
74 | oms->pdata.caps |= MMC_CAP_SD_HIGHSPEED; |
75 | if (device_property_read_bool(dev, propname: "cap-mmc-highspeed" )) |
76 | oms->pdata.caps |= MMC_CAP_MMC_HIGHSPEED; |
77 | |
78 | dev->platform_data = &oms->pdata; |
79 | return dev->platform_data; |
80 | err_ocr: |
81 | kfree(objp: oms); |
82 | return NULL; |
83 | } |
84 | EXPORT_SYMBOL(mmc_spi_get_pdata); |
85 | |
86 | void mmc_spi_put_pdata(struct spi_device *spi) |
87 | { |
88 | struct device *dev = &spi->dev; |
89 | struct of_mmc_spi *oms = to_of_mmc_spi(dev); |
90 | |
91 | if (!dev->platform_data || !dev_fwnode(dev)) |
92 | return; |
93 | |
94 | kfree(objp: oms); |
95 | dev->platform_data = NULL; |
96 | } |
97 | EXPORT_SYMBOL(mmc_spi_put_pdata); |
98 | |