Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 1 | /* |
| 2 | * linux/drivers/mmc/core/sdio_bus.c |
| 3 | * |
| 4 | * Copyright 2007 Pierre Ossman |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; either version 2 of the License, or (at |
| 9 | * your option) any later version. |
| 10 | * |
| 11 | * SDIO function driver model |
| 12 | */ |
| 13 | |
| 14 | #include <linux/device.h> |
| 15 | #include <linux/err.h> |
Paul Gortmaker | 3ef77af | 2011-07-10 12:42:00 -0400 | [diff] [blame] | 16 | #include <linux/export.h> |
Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 17 | #include <linux/slab.h> |
Ohad Ben-Cohen | 80fd933 | 2010-10-02 13:54:09 +0200 | [diff] [blame] | 18 | #include <linux/pm_runtime.h> |
Ulf Hansson | f48c767 | 2014-09-29 13:58:47 +0200 | [diff] [blame] | 19 | #include <linux/pm_domain.h> |
Aaron Lu | eed222a | 2013-03-05 11:24:52 +0800 | [diff] [blame] | 20 | #include <linux/acpi.h> |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 21 | |
| 22 | #include <linux/mmc/card.h> |
Ohad Ben-Cohen | ed919b0 | 2010-11-19 09:29:09 +0200 | [diff] [blame] | 23 | #include <linux/mmc/host.h> |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 24 | #include <linux/mmc/sdio_func.h> |
Sascha Hauer | 25185f3 | 2014-06-30 11:07:25 +0200 | [diff] [blame] | 25 | #include <linux/of.h> |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 26 | |
Sascha Hauer | 25185f3 | 2014-06-30 11:07:25 +0200 | [diff] [blame] | 27 | #include "core.h" |
Nicolas Pitre | b1538bc | 2007-06-16 02:06:47 -0400 | [diff] [blame] | 28 | #include "sdio_cis.h" |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 29 | #include "sdio_bus.h" |
| 30 | |
San Mehat | 2b6be3a | 2008-04-14 15:22:49 -0700 | [diff] [blame] | 31 | #ifdef CONFIG_MMC_EMBEDDED_SDIO |
| 32 | #include <linux/mmc/host.h> |
| 33 | #endif |
| 34 | |
Ulf Hansson | 433b7b1 | 2014-10-06 11:00:15 +0200 | [diff] [blame] | 35 | #define to_sdio_driver(d) container_of(d, struct sdio_driver, drv) |
| 36 | |
Pierre Ossman | bcfe66e | 2007-06-17 11:42:21 +0200 | [diff] [blame] | 37 | /* show configuration fields */ |
| 38 | #define sdio_config_attr(field, format_string) \ |
| 39 | static ssize_t \ |
| 40 | field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ |
| 41 | { \ |
| 42 | struct sdio_func *func; \ |
| 43 | \ |
| 44 | func = dev_to_sdio_func (dev); \ |
| 45 | return sprintf (buf, format_string, func->field); \ |
Greg Kroah-Hartman | f24fc57 | 2013-10-06 23:55:43 -0700 | [diff] [blame] | 46 | } \ |
| 47 | static DEVICE_ATTR_RO(field) |
Pierre Ossman | bcfe66e | 2007-06-17 11:42:21 +0200 | [diff] [blame] | 48 | |
| 49 | sdio_config_attr(class, "0x%02x\n"); |
| 50 | sdio_config_attr(vendor, "0x%04x\n"); |
| 51 | sdio_config_attr(device, "0x%04x\n"); |
| 52 | |
| 53 | static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) |
| 54 | { |
| 55 | struct sdio_func *func = dev_to_sdio_func (dev); |
| 56 | |
| 57 | return sprintf(buf, "sdio:c%02Xv%04Xd%04X\n", |
| 58 | func->class, func->vendor, func->device); |
| 59 | } |
Greg Kroah-Hartman | f24fc57 | 2013-10-06 23:55:43 -0700 | [diff] [blame] | 60 | static DEVICE_ATTR_RO(modalias); |
Pierre Ossman | bcfe66e | 2007-06-17 11:42:21 +0200 | [diff] [blame] | 61 | |
Greg Kroah-Hartman | f24fc57 | 2013-10-06 23:55:43 -0700 | [diff] [blame] | 62 | static struct attribute *sdio_dev_attrs[] = { |
| 63 | &dev_attr_class.attr, |
| 64 | &dev_attr_vendor.attr, |
| 65 | &dev_attr_device.attr, |
| 66 | &dev_attr_modalias.attr, |
| 67 | NULL, |
Pierre Ossman | bcfe66e | 2007-06-17 11:42:21 +0200 | [diff] [blame] | 68 | }; |
Greg Kroah-Hartman | f24fc57 | 2013-10-06 23:55:43 -0700 | [diff] [blame] | 69 | ATTRIBUTE_GROUPS(sdio_dev); |
Pierre Ossman | bcfe66e | 2007-06-17 11:42:21 +0200 | [diff] [blame] | 70 | |
Pierre Ossman | 3b38bea | 2007-06-16 15:54:55 +0200 | [diff] [blame] | 71 | static const struct sdio_device_id *sdio_match_one(struct sdio_func *func, |
| 72 | const struct sdio_device_id *id) |
| 73 | { |
| 74 | if (id->class != (__u8)SDIO_ANY_ID && id->class != func->class) |
| 75 | return NULL; |
| 76 | if (id->vendor != (__u16)SDIO_ANY_ID && id->vendor != func->vendor) |
| 77 | return NULL; |
| 78 | if (id->device != (__u16)SDIO_ANY_ID && id->device != func->device) |
| 79 | return NULL; |
| 80 | return id; |
| 81 | } |
| 82 | |
| 83 | static const struct sdio_device_id *sdio_match_device(struct sdio_func *func, |
| 84 | struct sdio_driver *sdrv) |
| 85 | { |
| 86 | const struct sdio_device_id *ids; |
| 87 | |
| 88 | ids = sdrv->id_table; |
| 89 | |
| 90 | if (ids) { |
| 91 | while (ids->class || ids->vendor || ids->device) { |
| 92 | if (sdio_match_one(func, ids)) |
| 93 | return ids; |
| 94 | ids++; |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | return NULL; |
| 99 | } |
| 100 | |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 101 | static int sdio_bus_match(struct device *dev, struct device_driver *drv) |
| 102 | { |
Pierre Ossman | 3b38bea | 2007-06-16 15:54:55 +0200 | [diff] [blame] | 103 | struct sdio_func *func = dev_to_sdio_func(dev); |
| 104 | struct sdio_driver *sdrv = to_sdio_driver(drv); |
| 105 | |
| 106 | if (sdio_match_device(func, sdrv)) |
| 107 | return 1; |
| 108 | |
| 109 | return 0; |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 110 | } |
| 111 | |
| 112 | static int |
Al Viro | 7ac0326 | 2007-10-14 05:46:09 +0100 | [diff] [blame] | 113 | sdio_bus_uevent(struct device *dev, struct kobj_uevent_env *env) |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 114 | { |
Pierre Ossman | d59b66c | 2007-06-17 11:34:23 +0200 | [diff] [blame] | 115 | struct sdio_func *func = dev_to_sdio_func(dev); |
Pierre Ossman | d59b66c | 2007-06-17 11:34:23 +0200 | [diff] [blame] | 116 | |
Al Viro | 7ac0326 | 2007-10-14 05:46:09 +0100 | [diff] [blame] | 117 | if (add_uevent_var(env, |
Pierre Ossman | d59b66c | 2007-06-17 11:34:23 +0200 | [diff] [blame] | 118 | "SDIO_CLASS=%02X", func->class)) |
| 119 | return -ENOMEM; |
| 120 | |
Al Viro | 7ac0326 | 2007-10-14 05:46:09 +0100 | [diff] [blame] | 121 | if (add_uevent_var(env, |
Pierre Ossman | d59b66c | 2007-06-17 11:34:23 +0200 | [diff] [blame] | 122 | "SDIO_ID=%04X:%04X", func->vendor, func->device)) |
| 123 | return -ENOMEM; |
| 124 | |
Al Viro | 7ac0326 | 2007-10-14 05:46:09 +0100 | [diff] [blame] | 125 | if (add_uevent_var(env, |
Pierre Ossman | d59b66c | 2007-06-17 11:34:23 +0200 | [diff] [blame] | 126 | "MODALIAS=sdio:c%02Xv%04Xd%04X", |
| 127 | func->class, func->vendor, func->device)) |
| 128 | return -ENOMEM; |
| 129 | |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 130 | return 0; |
| 131 | } |
| 132 | |
| 133 | static int sdio_bus_probe(struct device *dev) |
| 134 | { |
Pierre Ossman | 3b38bea | 2007-06-16 15:54:55 +0200 | [diff] [blame] | 135 | struct sdio_driver *drv = to_sdio_driver(dev->driver); |
| 136 | struct sdio_func *func = dev_to_sdio_func(dev); |
| 137 | const struct sdio_device_id *id; |
David Vrabel | 9a08f82 | 2007-08-08 14:23:48 +0100 | [diff] [blame] | 138 | int ret; |
Pierre Ossman | 3b38bea | 2007-06-16 15:54:55 +0200 | [diff] [blame] | 139 | |
| 140 | id = sdio_match_device(func, drv); |
| 141 | if (!id) |
| 142 | return -ENODEV; |
| 143 | |
Ulf Hansson | 1ef48e3 | 2015-06-01 12:18:25 +0200 | [diff] [blame] | 144 | ret = dev_pm_domain_attach(dev, false); |
| 145 | if (ret == -EPROBE_DEFER) |
| 146 | return ret; |
| 147 | |
Ohad Ben-Cohen | 40bba0c | 2010-10-02 13:54:11 +0200 | [diff] [blame] | 148 | /* Unbound SDIO functions are always suspended. |
| 149 | * During probe, the function is set active and the usage count |
| 150 | * is incremented. If the driver supports runtime PM, |
| 151 | * it should call pm_runtime_put_noidle() in its probe routine and |
| 152 | * pm_runtime_get_noresume() in its remove routine. |
| 153 | */ |
Ohad Ben-Cohen | ed919b0 | 2010-11-19 09:29:09 +0200 | [diff] [blame] | 154 | if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) { |
| 155 | ret = pm_runtime_get_sync(dev); |
| 156 | if (ret < 0) |
Li Fei | 3bffb80 | 2013-04-08 09:36:39 +0800 | [diff] [blame] | 157 | goto disable_runtimepm; |
Ohad Ben-Cohen | ed919b0 | 2010-11-19 09:29:09 +0200 | [diff] [blame] | 158 | } |
Ohad Ben-Cohen | 40bba0c | 2010-10-02 13:54:11 +0200 | [diff] [blame] | 159 | |
David Vrabel | 9a08f82 | 2007-08-08 14:23:48 +0100 | [diff] [blame] | 160 | /* Set the default block size so the driver is sure it's something |
| 161 | * sensible. */ |
| 162 | sdio_claim_host(func); |
| 163 | ret = sdio_set_block_size(func, 0); |
| 164 | sdio_release_host(func); |
| 165 | if (ret) |
Ohad Ben-Cohen | 40bba0c | 2010-10-02 13:54:11 +0200 | [diff] [blame] | 166 | goto disable_runtimepm; |
David Vrabel | 9a08f82 | 2007-08-08 14:23:48 +0100 | [diff] [blame] | 167 | |
Ohad Ben-Cohen | 40bba0c | 2010-10-02 13:54:11 +0200 | [diff] [blame] | 168 | ret = drv->probe(func, id); |
| 169 | if (ret) |
| 170 | goto disable_runtimepm; |
| 171 | |
| 172 | return 0; |
| 173 | |
| 174 | disable_runtimepm: |
Ohad Ben-Cohen | ed919b0 | 2010-11-19 09:29:09 +0200 | [diff] [blame] | 175 | if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) |
| 176 | pm_runtime_put_noidle(dev); |
Ulf Hansson | 1ef48e3 | 2015-06-01 12:18:25 +0200 | [diff] [blame] | 177 | dev_pm_domain_detach(dev, false); |
Ohad Ben-Cohen | 40bba0c | 2010-10-02 13:54:11 +0200 | [diff] [blame] | 178 | return ret; |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 179 | } |
| 180 | |
| 181 | static int sdio_bus_remove(struct device *dev) |
| 182 | { |
Pierre Ossman | 3b38bea | 2007-06-16 15:54:55 +0200 | [diff] [blame] | 183 | struct sdio_driver *drv = to_sdio_driver(dev->driver); |
| 184 | struct sdio_func *func = dev_to_sdio_func(dev); |
Ohad Ben-Cohen | ed919b0 | 2010-11-19 09:29:09 +0200 | [diff] [blame] | 185 | int ret = 0; |
Ohad Ben-Cohen | 40bba0c | 2010-10-02 13:54:11 +0200 | [diff] [blame] | 186 | |
| 187 | /* Make sure card is powered before invoking ->remove() */ |
Ohad Ben-Cohen | ecc02441 | 2011-07-17 16:38:21 +0100 | [diff] [blame] | 188 | if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) |
| 189 | pm_runtime_get_sync(dev); |
Pierre Ossman | 3b38bea | 2007-06-16 15:54:55 +0200 | [diff] [blame] | 190 | |
| 191 | drv->remove(func); |
| 192 | |
Nicolas Pitre | d1496c3 | 2007-06-30 16:29:41 +0200 | [diff] [blame] | 193 | if (func->irq_handler) { |
Joe Perches | 6606110 | 2014-09-12 14:56:56 -0700 | [diff] [blame] | 194 | pr_warn("WARNING: driver %s did not remove its interrupt handler!\n", |
| 195 | drv->name); |
Nicolas Pitre | d1496c3 | 2007-06-30 16:29:41 +0200 | [diff] [blame] | 196 | sdio_claim_host(func); |
| 197 | sdio_release_irq(func); |
| 198 | sdio_release_host(func); |
| 199 | } |
| 200 | |
Ohad Ben-Cohen | 40bba0c | 2010-10-02 13:54:11 +0200 | [diff] [blame] | 201 | /* First, undo the increment made directly above */ |
Ohad Ben-Cohen | ed919b0 | 2010-11-19 09:29:09 +0200 | [diff] [blame] | 202 | if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) |
| 203 | pm_runtime_put_noidle(dev); |
Ohad Ben-Cohen | 40bba0c | 2010-10-02 13:54:11 +0200 | [diff] [blame] | 204 | |
| 205 | /* Then undo the runtime PM settings in sdio_bus_probe() */ |
Ohad Ben-Cohen | ed919b0 | 2010-11-19 09:29:09 +0200 | [diff] [blame] | 206 | if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) |
Ohad Ben-Cohen | 297c7f2 | 2011-06-09 23:40:27 +0000 | [diff] [blame] | 207 | pm_runtime_put_sync(dev); |
Ohad Ben-Cohen | 40bba0c | 2010-10-02 13:54:11 +0200 | [diff] [blame] | 208 | |
Ulf Hansson | 1ef48e3 | 2015-06-01 12:18:25 +0200 | [diff] [blame] | 209 | dev_pm_domain_detach(dev, false); |
| 210 | |
Ohad Ben-Cohen | 40bba0c | 2010-10-02 13:54:11 +0200 | [diff] [blame] | 211 | return ret; |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 212 | } |
| 213 | |
Ohad Ben-Cohen | 80fd933 | 2010-10-02 13:54:09 +0200 | [diff] [blame] | 214 | static const struct dev_pm_ops sdio_bus_pm_ops = { |
Ulf Hansson | 573185c | 2014-02-28 12:49:00 +0100 | [diff] [blame] | 215 | SET_SYSTEM_SLEEP_PM_OPS(pm_generic_suspend, pm_generic_resume) |
Ohad Ben-Cohen | 80fd933 | 2010-10-02 13:54:09 +0200 | [diff] [blame] | 216 | SET_RUNTIME_PM_OPS( |
| 217 | pm_generic_runtime_suspend, |
| 218 | pm_generic_runtime_resume, |
Rafael J. Wysocki | 45f0a85 | 2013-06-03 21:49:52 +0200 | [diff] [blame] | 219 | NULL |
Ohad Ben-Cohen | 80fd933 | 2010-10-02 13:54:09 +0200 | [diff] [blame] | 220 | ) |
| 221 | }; |
| 222 | |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 223 | static struct bus_type sdio_bus_type = { |
| 224 | .name = "sdio", |
Greg Kroah-Hartman | f24fc57 | 2013-10-06 23:55:43 -0700 | [diff] [blame] | 225 | .dev_groups = sdio_dev_groups, |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 226 | .match = sdio_bus_match, |
| 227 | .uevent = sdio_bus_uevent, |
| 228 | .probe = sdio_bus_probe, |
| 229 | .remove = sdio_bus_remove, |
Ulf Hansson | d99903c | 2014-10-06 10:28:35 +0200 | [diff] [blame] | 230 | .pm = &sdio_bus_pm_ops, |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 231 | }; |
| 232 | |
| 233 | int sdio_register_bus(void) |
| 234 | { |
| 235 | return bus_register(&sdio_bus_type); |
| 236 | } |
| 237 | |
| 238 | void sdio_unregister_bus(void) |
| 239 | { |
| 240 | bus_unregister(&sdio_bus_type); |
| 241 | } |
| 242 | |
Pierre Ossman | f76c851 | 2007-05-27 12:00:02 +0200 | [diff] [blame] | 243 | /** |
| 244 | * sdio_register_driver - register a function driver |
| 245 | * @drv: SDIO function driver |
| 246 | */ |
| 247 | int sdio_register_driver(struct sdio_driver *drv) |
| 248 | { |
| 249 | drv->drv.name = drv->name; |
| 250 | drv->drv.bus = &sdio_bus_type; |
| 251 | return driver_register(&drv->drv); |
| 252 | } |
| 253 | EXPORT_SYMBOL_GPL(sdio_register_driver); |
| 254 | |
| 255 | /** |
| 256 | * sdio_unregister_driver - unregister a function driver |
| 257 | * @drv: SDIO function driver |
| 258 | */ |
| 259 | void sdio_unregister_driver(struct sdio_driver *drv) |
| 260 | { |
| 261 | drv->drv.bus = &sdio_bus_type; |
| 262 | driver_unregister(&drv->drv); |
| 263 | } |
| 264 | EXPORT_SYMBOL_GPL(sdio_unregister_driver); |
| 265 | |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 266 | static void sdio_release_func(struct device *dev) |
| 267 | { |
| 268 | struct sdio_func *func = dev_to_sdio_func(dev); |
| 269 | |
San Mehat | 2b6be3a | 2008-04-14 15:22:49 -0700 | [diff] [blame] | 270 | #ifdef CONFIG_MMC_EMBEDDED_SDIO |
| 271 | /* |
| 272 | * If this device is embedded then we never allocated |
| 273 | * cis tables for this func |
| 274 | */ |
| 275 | if (!func->card->host->embedded_sdio_data.funcs) |
| 276 | #endif |
| 277 | sdio_free_func_cis(func); |
Nicolas Pitre | b1538bc | 2007-06-16 02:06:47 -0400 | [diff] [blame] | 278 | |
Sachin Kamat | 4c42d6c | 2012-11-20 14:43:16 +0530 | [diff] [blame] | 279 | kfree(func->info); |
Heiner Kallweit | 727a153 | 2017-03-29 20:54:37 +0200 | [diff] [blame] | 280 | kfree(func->tmpbuf); |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 281 | kfree(func); |
| 282 | } |
| 283 | |
| 284 | /* |
| 285 | * Allocate and initialise a new SDIO function structure. |
| 286 | */ |
| 287 | struct sdio_func *sdio_alloc_func(struct mmc_card *card) |
| 288 | { |
| 289 | struct sdio_func *func; |
| 290 | |
Mariusz Kozlowski | 9f2fcf9 | 2007-08-01 00:05:24 +0200 | [diff] [blame] | 291 | func = kzalloc(sizeof(struct sdio_func), GFP_KERNEL); |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 292 | if (!func) |
| 293 | return ERR_PTR(-ENOMEM); |
| 294 | |
Heiner Kallweit | 727a153 | 2017-03-29 20:54:37 +0200 | [diff] [blame] | 295 | /* |
| 296 | * allocate buffer separately to make sure it's properly aligned for |
| 297 | * DMA usage (incl. 64 bit DMA) |
| 298 | */ |
| 299 | func->tmpbuf = kmalloc(4, GFP_KERNEL); |
| 300 | if (!func->tmpbuf) { |
| 301 | kfree(func); |
| 302 | return ERR_PTR(-ENOMEM); |
| 303 | } |
| 304 | |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 305 | func->card = card; |
| 306 | |
| 307 | device_initialize(&func->dev); |
| 308 | |
| 309 | func->dev.parent = &card->dev; |
| 310 | func->dev.bus = &sdio_bus_type; |
| 311 | func->dev.release = sdio_release_func; |
| 312 | |
| 313 | return func; |
| 314 | } |
| 315 | |
Aaron Lu | eed222a | 2013-03-05 11:24:52 +0800 | [diff] [blame] | 316 | #ifdef CONFIG_ACPI |
| 317 | static void sdio_acpi_set_handle(struct sdio_func *func) |
| 318 | { |
| 319 | struct mmc_host *host = func->card->host; |
Dan Carpenter | 51d3460 | 2014-10-23 14:37:00 +0300 | [diff] [blame] | 320 | u64 addr = ((u64)host->slotno << 16) | func->num; |
Aaron Lu | eed222a | 2013-03-05 11:24:52 +0800 | [diff] [blame] | 321 | |
Rafael J. Wysocki | 9c5ad36 | 2013-11-28 23:58:28 +0100 | [diff] [blame] | 322 | acpi_preset_companion(&func->dev, ACPI_COMPANION(host->parent), addr); |
Aaron Lu | eed222a | 2013-03-05 11:24:52 +0800 | [diff] [blame] | 323 | } |
| 324 | #else |
| 325 | static inline void sdio_acpi_set_handle(struct sdio_func *func) {} |
| 326 | #endif |
| 327 | |
Sascha Hauer | 25185f3 | 2014-06-30 11:07:25 +0200 | [diff] [blame] | 328 | static void sdio_set_of_node(struct sdio_func *func) |
| 329 | { |
| 330 | struct mmc_host *host = func->card->host; |
| 331 | |
| 332 | func->dev.of_node = mmc_of_find_child_device(host, func->num); |
| 333 | } |
| 334 | |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 335 | /* |
| 336 | * Register a new SDIO function with the driver model. |
| 337 | */ |
| 338 | int sdio_add_func(struct sdio_func *func) |
| 339 | { |
| 340 | int ret; |
| 341 | |
Kay Sievers | d1b2686 | 2008-11-08 21:37:46 +0100 | [diff] [blame] | 342 | dev_set_name(&func->dev, "%s:%d", mmc_card_id(func->card), func->num); |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 343 | |
Sascha Hauer | 25185f3 | 2014-06-30 11:07:25 +0200 | [diff] [blame] | 344 | sdio_set_of_node(func); |
Aaron Lu | eed222a | 2013-03-05 11:24:52 +0800 | [diff] [blame] | 345 | sdio_acpi_set_handle(func); |
Fu, Zhonghui | ec076cd | 2015-12-04 21:05:56 +0800 | [diff] [blame] | 346 | device_enable_async_suspend(&func->dev); |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 347 | ret = device_add(&func->dev); |
Ulf Hansson | 1ef48e3 | 2015-06-01 12:18:25 +0200 | [diff] [blame] | 348 | if (ret == 0) |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 349 | sdio_func_set_present(func); |
| 350 | |
| 351 | return ret; |
| 352 | } |
| 353 | |
| 354 | /* |
| 355 | * Unregister a SDIO function with the driver model, and |
| 356 | * (eventually) free it. |
Daniel Drake | 3d10a1b | 2009-12-17 15:27:17 -0800 | [diff] [blame] | 357 | * This function can be called through error paths where sdio_add_func() was |
| 358 | * never executed (because a failure occurred at an earlier point). |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 359 | */ |
| 360 | void sdio_remove_func(struct sdio_func *func) |
| 361 | { |
Daniel Drake | 3d10a1b | 2009-12-17 15:27:17 -0800 | [diff] [blame] | 362 | if (!sdio_func_present(func)) |
| 363 | return; |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 364 | |
Daniel Drake | 3d10a1b | 2009-12-17 15:27:17 -0800 | [diff] [blame] | 365 | device_del(&func->dev); |
Sascha Hauer | 25185f3 | 2014-06-30 11:07:25 +0200 | [diff] [blame] | 366 | of_node_put(func->dev.of_node); |
Pierre Ossman | e29a7d7 | 2007-05-26 13:48:18 +0200 | [diff] [blame] | 367 | put_device(&func->dev); |
| 368 | } |
| 369 | |