diff options
| author | Russell King <rmk+kernel@arm.linux.org.uk> | 2014-02-28 16:32:34 -0500 |
|---|---|---|
| committer | Chris Ball <chris@printf.net> | 2014-03-04 16:28:39 -0500 |
| commit | fcdb7c8f5019f77b1f55739a1caf9168d3b455d4 (patch) | |
| tree | 521af2903ea711ab26d85289d4a4efab48d427d4 | |
| parent | 142dbab951fb9d2281e69f8d7522e6b7e0553ec0 (diff) | |
mmc: sdhci-spear: fix platform_data usage
sdhci-spear is unsafe should a probe fail or defer, since it overwrites
the platform_data with its own driver-private data. It's trivial to
fix as SDHCI allows for driver-private data to be appended to its own
structure - we just need to arrange the code to allow this.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Chris Ball <chris@printf.net>
| -rw-r--r-- | drivers/mmc/host/sdhci-spear.c | 43 |
1 files changed, 17 insertions, 26 deletions
diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index 394714f71b2f..d89267eb14b6 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c | |||
| @@ -105,6 +105,7 @@ static int sdhci_probe(struct platform_device *pdev) | |||
| 105 | struct sdhci_host *host; | 105 | struct sdhci_host *host; |
| 106 | struct resource *iomem; | 106 | struct resource *iomem; |
| 107 | struct spear_sdhci *sdhci; | 107 | struct spear_sdhci *sdhci; |
| 108 | struct device *dev; | ||
| 108 | int ret; | 109 | int ret; |
| 109 | 110 | ||
| 110 | iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 111 | iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| @@ -121,25 +122,28 @@ static int sdhci_probe(struct platform_device *pdev) | |||
| 121 | goto err; | 122 | goto err; |
| 122 | } | 123 | } |
| 123 | 124 | ||
| 124 | sdhci = devm_kzalloc(&pdev->dev, sizeof(*sdhci), GFP_KERNEL); | 125 | dev = pdev->dev.parent ? pdev->dev.parent : &pdev->dev; |
| 125 | if (!sdhci) { | 126 | host = sdhci_alloc_host(dev, sizeof(*sdhci)); |
| 126 | ret = -ENOMEM; | 127 | if (IS_ERR(host)) { |
| 128 | ret = PTR_ERR(host); | ||
| 127 | dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n"); | 129 | dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n"); |
| 128 | goto err; | 130 | goto err; |
| 129 | } | 131 | } |
| 130 | 132 | ||
| 133 | sdhci = sdhci_priv(host); | ||
| 134 | |||
| 131 | /* clk enable */ | 135 | /* clk enable */ |
| 132 | sdhci->clk = devm_clk_get(&pdev->dev, NULL); | 136 | sdhci->clk = devm_clk_get(&pdev->dev, NULL); |
| 133 | if (IS_ERR(sdhci->clk)) { | 137 | if (IS_ERR(sdhci->clk)) { |
| 134 | ret = PTR_ERR(sdhci->clk); | 138 | ret = PTR_ERR(sdhci->clk); |
| 135 | dev_dbg(&pdev->dev, "Error getting clock\n"); | 139 | dev_dbg(&pdev->dev, "Error getting clock\n"); |
| 136 | goto err; | 140 | goto err_host; |
| 137 | } | 141 | } |
| 138 | 142 | ||
| 139 | ret = clk_prepare_enable(sdhci->clk); | 143 | ret = clk_prepare_enable(sdhci->clk); |
| 140 | if (ret) { | 144 | if (ret) { |
| 141 | dev_dbg(&pdev->dev, "Error enabling clock\n"); | 145 | dev_dbg(&pdev->dev, "Error enabling clock\n"); |
| 142 | goto err; | 146 | goto err_host; |
| 143 | } | 147 | } |
| 144 | 148 | ||
| 145 | ret = clk_set_rate(sdhci->clk, 50000000); | 149 | ret = clk_set_rate(sdhci->clk, 50000000); |
| @@ -157,19 +161,6 @@ static int sdhci_probe(struct platform_device *pdev) | |||
| 157 | sdhci->data = dev_get_platdata(&pdev->dev); | 161 | sdhci->data = dev_get_platdata(&pdev->dev); |
| 158 | } | 162 | } |
| 159 | 163 | ||
| 160 | pdev->dev.platform_data = sdhci; | ||
| 161 | |||
| 162 | if (pdev->dev.parent) | ||
| 163 | host = sdhci_alloc_host(pdev->dev.parent, 0); | ||
| 164 | else | ||
| 165 | host = sdhci_alloc_host(&pdev->dev, 0); | ||
| 166 | |||
| 167 | if (IS_ERR(host)) { | ||
| 168 | ret = PTR_ERR(host); | ||
| 169 | dev_dbg(&pdev->dev, "error allocating host\n"); | ||
| 170 | goto disable_clk; | ||
| 171 | } | ||
| 172 | |||
| 173 | host->hw_name = "sdhci"; | 164 | host->hw_name = "sdhci"; |
| 174 | host->ops = &sdhci_pltfm_ops; | 165 | host->ops = &sdhci_pltfm_ops; |
| 175 | host->irq = platform_get_irq(pdev, 0); | 166 | host->irq = platform_get_irq(pdev, 0); |
| @@ -180,13 +171,13 @@ static int sdhci_probe(struct platform_device *pdev) | |||
| 180 | if (!host->ioaddr) { | 171 | if (!host->ioaddr) { |
| 181 | ret = -ENOMEM; | 172 | ret = -ENOMEM; |
| 182 | dev_dbg(&pdev->dev, "failed to remap registers\n"); | 173 | dev_dbg(&pdev->dev, "failed to remap registers\n"); |
| 183 | goto free_host; | 174 | goto disable_clk; |
| 184 | } | 175 | } |
| 185 | 176 | ||
| 186 | ret = sdhci_add_host(host); | 177 | ret = sdhci_add_host(host); |
| 187 | if (ret) { | 178 | if (ret) { |
| 188 | dev_dbg(&pdev->dev, "error adding host\n"); | 179 | dev_dbg(&pdev->dev, "error adding host\n"); |
| 189 | goto free_host; | 180 | goto disable_clk; |
| 190 | } | 181 | } |
| 191 | 182 | ||
| 192 | platform_set_drvdata(pdev, host); | 183 | platform_set_drvdata(pdev, host); |
| @@ -257,10 +248,10 @@ static int sdhci_probe(struct platform_device *pdev) | |||
| 257 | 248 | ||
| 258 | set_drvdata: | 249 | set_drvdata: |
| 259 | sdhci_remove_host(host, 1); | 250 | sdhci_remove_host(host, 1); |
| 260 | free_host: | ||
| 261 | sdhci_free_host(host); | ||
| 262 | disable_clk: | 251 | disable_clk: |
| 263 | clk_disable_unprepare(sdhci->clk); | 252 | clk_disable_unprepare(sdhci->clk); |
| 253 | err_host: | ||
| 254 | sdhci_free_host(host); | ||
| 264 | err: | 255 | err: |
| 265 | dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret); | 256 | dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret); |
| 266 | return ret; | 257 | return ret; |
| @@ -269,7 +260,7 @@ err: | |||
| 269 | static int sdhci_remove(struct platform_device *pdev) | 260 | static int sdhci_remove(struct platform_device *pdev) |
| 270 | { | 261 | { |
| 271 | struct sdhci_host *host = platform_get_drvdata(pdev); | 262 | struct sdhci_host *host = platform_get_drvdata(pdev); |
| 272 | struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev); | 263 | struct spear_sdhci *sdhci = sdhci_priv(host); |
| 273 | int dead = 0; | 264 | int dead = 0; |
| 274 | u32 scratch; | 265 | u32 scratch; |
| 275 | 266 | ||
| @@ -278,8 +269,8 @@ static int sdhci_remove(struct platform_device *pdev) | |||
| 278 | dead = 1; | 269 | dead = 1; |
| 279 | 270 | ||
| 280 | sdhci_remove_host(host, dead); | 271 | sdhci_remove_host(host, dead); |
| 281 | sdhci_free_host(host); | ||
| 282 | clk_disable_unprepare(sdhci->clk); | 272 | clk_disable_unprepare(sdhci->clk); |
| 273 | sdhci_free_host(host); | ||
| 283 | 274 | ||
| 284 | return 0; | 275 | return 0; |
| 285 | } | 276 | } |
| @@ -288,7 +279,7 @@ static int sdhci_remove(struct platform_device *pdev) | |||
| 288 | static int sdhci_suspend(struct device *dev) | 279 | static int sdhci_suspend(struct device *dev) |
| 289 | { | 280 | { |
| 290 | struct sdhci_host *host = dev_get_drvdata(dev); | 281 | struct sdhci_host *host = dev_get_drvdata(dev); |
| 291 | struct spear_sdhci *sdhci = dev_get_platdata(dev); | 282 | struct spear_sdhci *sdhci = sdhci_priv(host); |
| 292 | int ret; | 283 | int ret; |
| 293 | 284 | ||
| 294 | ret = sdhci_suspend_host(host); | 285 | ret = sdhci_suspend_host(host); |
| @@ -301,7 +292,7 @@ static int sdhci_suspend(struct device *dev) | |||
| 301 | static int sdhci_resume(struct device *dev) | 292 | static int sdhci_resume(struct device *dev) |
| 302 | { | 293 | { |
| 303 | struct sdhci_host *host = dev_get_drvdata(dev); | 294 | struct sdhci_host *host = dev_get_drvdata(dev); |
| 304 | struct spear_sdhci *sdhci = dev_get_platdata(dev); | 295 | struct spear_sdhci *sdhci = sdhci_priv(host); |
| 305 | int ret; | 296 | int ret; |
| 306 | 297 | ||
| 307 | ret = clk_enable(sdhci->clk); | 298 | ret = clk_enable(sdhci->clk); |
