Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mmc: basic SDIO device model

Add the sdio bus type and basic device handling.

Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>

+279 -15
+1 -1
drivers/mmc/core/Makefile
··· 9 9 obj-$(CONFIG_MMC) += mmc_core.o 10 10 mmc_core-y := core.o sysfs.o bus.o host.o \ 11 11 mmc.o mmc_ops.o sd.o sd_ops.o \ 12 - sdio.o sdio_ops.o 12 + sdio.o sdio_ops.o sdio_bus.o 13 13
+22 -5
drivers/mmc/core/core.c
··· 29 29 #include "core.h" 30 30 #include "bus.h" 31 31 #include "host.h" 32 + #include "sdio_bus.h" 32 33 33 34 #include "mmc_ops.h" 34 35 #include "sd_ops.h" ··· 740 739 return -ENOMEM; 741 740 742 741 ret = mmc_register_bus(); 743 - if (ret == 0) { 744 - ret = mmc_register_host_class(); 745 - if (ret) 746 - mmc_unregister_bus(); 747 - } 742 + if (ret) 743 + goto destroy_workqueue; 744 + 745 + ret = mmc_register_host_class(); 746 + if (ret) 747 + goto unregister_bus; 748 + 749 + ret = sdio_register_bus(); 750 + if (ret) 751 + goto unregister_host_class; 752 + 753 + return 0; 754 + 755 + unregister_host_class: 756 + mmc_unregister_host_class(); 757 + unregister_bus: 758 + mmc_unregister_bus(); 759 + destroy_workqueue: 760 + destroy_workqueue(workqueue); 761 + 748 762 return ret; 749 763 } 750 764 751 765 static void __exit mmc_exit(void) 752 766 { 767 + sdio_unregister_bus(); 753 768 mmc_unregister_host_class(); 754 769 mmc_unregister_bus(); 755 770 destroy_workqueue(workqueue);
+63 -9
drivers/mmc/core/sdio.c
··· 13 13 14 14 #include <linux/mmc/host.h> 15 15 #include <linux/mmc/card.h> 16 + #include <linux/mmc/sdio_func.h> 16 17 17 18 #include "core.h" 18 19 #include "bus.h" 20 + #include "sdio_bus.h" 19 21 #include "mmc_ops.h" 20 22 #include "sd_ops.h" 21 23 #include "sdio_ops.h" 24 + 25 + static int sdio_init_func(struct mmc_card *card, unsigned int fn) 26 + { 27 + struct sdio_func *func; 28 + 29 + BUG_ON(fn > SDIO_MAX_FUNCS); 30 + 31 + func = sdio_alloc_func(card); 32 + if (IS_ERR(func)) 33 + return PTR_ERR(func); 34 + 35 + func->num = fn; 36 + 37 + card->sdio_func[fn - 1] = func; 38 + 39 + return 0; 40 + } 22 41 23 42 /* 24 43 * Host is being removed. Free up the current card. 25 44 */ 26 45 static void mmc_sdio_remove(struct mmc_host *host) 27 46 { 47 + int i; 48 + 28 49 BUG_ON(!host); 29 50 BUG_ON(!host->card); 51 + 52 + for (i = 0;i < host->card->sdio_funcs;i++) { 53 + if (host->card->sdio_func[i]) { 54 + sdio_remove_func(host->card->sdio_func[i]); 55 + host->card->sdio_func[i] = NULL; 56 + } 57 + } 30 58 31 59 mmc_remove_card(host->card); 32 60 host->card = NULL; ··· 101 73 int mmc_attach_sdio(struct mmc_host *host, u32 ocr) 102 74 { 103 75 int err; 104 - int funcs; 76 + int i, funcs; 105 77 struct mmc_card *card; 106 78 107 79 BUG_ON(!host); ··· 160 132 } 161 133 162 134 card->type = MMC_TYPE_SDIO; 135 + card->sdio_funcs = funcs; 136 + 137 + host->card = card; 163 138 164 139 /* 165 140 * Set card RCA. 166 141 */ 167 142 err = mmc_send_relative_addr(host, &card->rca); 168 143 if (err) 169 - goto free_card; 144 + goto remove; 170 145 171 146 mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); 172 147 ··· 178 147 */ 179 148 err = mmc_select_card(card); 180 149 if (err) 181 - goto free_card; 150 + goto remove; 182 151 183 - host->card = card; 152 + /* 153 + * Initialize (but don't add) all present functions. 154 + */ 155 + for (i = 0;i < funcs;i++) { 156 + err = sdio_init_func(host->card, i + 1); 157 + if (err) 158 + goto remove; 159 + } 184 160 185 161 mmc_release_host(host); 186 162 163 + /* 164 + * First add the card to the driver model... 165 + */ 187 166 err = mmc_add_card(host->card); 188 167 if (err) 189 - goto reclaim_host; 168 + goto remove_added; 169 + 170 + /* 171 + * ...then the SDIO functions. 172 + */ 173 + for (i = 0;i < funcs;i++) { 174 + err = sdio_add_func(host->card->sdio_func[i]); 175 + if (err) 176 + goto remove_added; 177 + } 190 178 191 179 return 0; 192 180 193 - reclaim_host: 181 + 182 + remove_added: 183 + /* Remove without lock if the device has been added. */ 184 + mmc_sdio_remove(host); 194 185 mmc_claim_host(host); 195 - free_card: 196 - mmc_remove_card(card); 197 - host->card = NULL; 186 + remove: 187 + /* And with lock if it hasn't been added. */ 188 + if (host->card) 189 + mmc_sdio_remove(host); 198 190 err: 199 191 mmc_detach_bus(host); 200 192 mmc_release_host(host);
+129
drivers/mmc/core/sdio_bus.c
··· 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> 16 + 17 + #include <linux/mmc/card.h> 18 + #include <linux/mmc/sdio_func.h> 19 + 20 + #include "sdio_bus.h" 21 + 22 + #define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev) 23 + 24 + /* 25 + * This currently matches any SDIO function to any driver in order 26 + * to help initial development and testing. 27 + */ 28 + static int sdio_bus_match(struct device *dev, struct device_driver *drv) 29 + { 30 + return 1; 31 + } 32 + 33 + static int 34 + sdio_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf, 35 + int buf_size) 36 + { 37 + envp[0] = NULL; 38 + 39 + return 0; 40 + } 41 + 42 + static int sdio_bus_probe(struct device *dev) 43 + { 44 + return -ENODEV; 45 + } 46 + 47 + static int sdio_bus_remove(struct device *dev) 48 + { 49 + return 0; 50 + } 51 + 52 + static struct bus_type sdio_bus_type = { 53 + .name = "sdio", 54 + .match = sdio_bus_match, 55 + .uevent = sdio_bus_uevent, 56 + .probe = sdio_bus_probe, 57 + .remove = sdio_bus_remove, 58 + }; 59 + 60 + int sdio_register_bus(void) 61 + { 62 + return bus_register(&sdio_bus_type); 63 + } 64 + 65 + void sdio_unregister_bus(void) 66 + { 67 + bus_unregister(&sdio_bus_type); 68 + } 69 + 70 + static void sdio_release_func(struct device *dev) 71 + { 72 + struct sdio_func *func = dev_to_sdio_func(dev); 73 + 74 + kfree(func); 75 + } 76 + 77 + /* 78 + * Allocate and initialise a new SDIO function structure. 79 + */ 80 + struct sdio_func *sdio_alloc_func(struct mmc_card *card) 81 + { 82 + struct sdio_func *func; 83 + 84 + func = kmalloc(sizeof(struct sdio_func), GFP_KERNEL); 85 + if (!func) 86 + return ERR_PTR(-ENOMEM); 87 + 88 + memset(func, 0, sizeof(struct sdio_func)); 89 + 90 + func->card = card; 91 + 92 + device_initialize(&func->dev); 93 + 94 + func->dev.parent = &card->dev; 95 + func->dev.bus = &sdio_bus_type; 96 + func->dev.release = sdio_release_func; 97 + 98 + return func; 99 + } 100 + 101 + /* 102 + * Register a new SDIO function with the driver model. 103 + */ 104 + int sdio_add_func(struct sdio_func *func) 105 + { 106 + int ret; 107 + 108 + snprintf(func->dev.bus_id, sizeof(func->dev.bus_id), 109 + "%s:%d", mmc_card_id(func->card), func->num); 110 + 111 + ret = device_add(&func->dev); 112 + if (ret == 0) 113 + sdio_func_set_present(func); 114 + 115 + return ret; 116 + } 117 + 118 + /* 119 + * Unregister a SDIO function with the driver model, and 120 + * (eventually) free it. 121 + */ 122 + void sdio_remove_func(struct sdio_func *func) 123 + { 124 + if (sdio_func_present(func)) 125 + device_del(&func->dev); 126 + 127 + put_device(&func->dev); 128 + } 129 +
+22
drivers/mmc/core/sdio_bus.h
··· 1 + /* 2 + * linux/drivers/mmc/core/sdio_bus.h 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 + #ifndef _MMC_CORE_SDIO_BUS_H 12 + #define _MMC_CORE_SDIO_BUS_H 13 + 14 + struct sdio_func *sdio_alloc_func(struct mmc_card *card); 15 + int sdio_add_func(struct sdio_func *func); 16 + void sdio_remove_func(struct sdio_func *func); 17 + 18 + int sdio_register_bus(void); 19 + void sdio_unregister_bus(void); 20 + 21 + #endif 22 +
+7
include/linux/mmc/card.h
··· 56 56 }; 57 57 58 58 struct mmc_host; 59 + struct sdio_func; 60 + 61 + #define SDIO_MAX_FUNCS 7 59 62 60 63 /* 61 64 * MMC device ··· 76 73 #define MMC_STATE_READONLY (1<<1) /* card is read-only */ 77 74 #define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */ 78 75 #define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */ 76 + 79 77 u32 raw_cid[4]; /* raw card CID */ 80 78 u32 raw_csd[4]; /* raw card CSD */ 81 79 u32 raw_scr[2]; /* raw card SCR */ ··· 85 81 struct mmc_ext_csd ext_csd; /* mmc v4 extended card specific */ 86 82 struct sd_scr scr; /* extra SD information */ 87 83 struct sd_switch_caps sw_caps; /* switch (CMD6) caps */ 84 + 85 + unsigned int sdio_funcs; /* number of SDIO functions */ 86 + struct sdio_func *sdio_func[SDIO_MAX_FUNCS]; /* SDIO functions (devices) */ 88 87 }; 89 88 90 89 #define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC)
+35
include/linux/mmc/sdio_func.h
··· 1 + /* 2 + * include/linux/mmc/sdio_func.h 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 + 12 + #ifndef MMC_SDIO_FUNC_H 13 + #define MMC_SDIO_FUNC_H 14 + 15 + struct mmc_card; 16 + 17 + /* 18 + * SDIO function devices 19 + */ 20 + struct sdio_func { 21 + struct mmc_card *card; /* the card this device belongs to */ 22 + struct device dev; /* the device */ 23 + unsigned int num; /* function number */ 24 + unsigned int state; /* function state */ 25 + #define SDIO_STATE_PRESENT (1<<0) /* present in sysfs */ 26 + }; 27 + 28 + #define sdio_func_present(f) ((f)->state & SDIO_STATE_PRESENT) 29 + 30 + #define sdio_func_set_present(f) ((f)->state |= SDIO_STATE_PRESENT) 31 + 32 + #define sdio_func_id(f) ((f)->dev.bus_id) 33 + 34 + #endif 35 +