diff options
| author | Hans de Goede <hdegoede@redhat.com> | 2014-02-22 10:53:34 -0500 |
|---|---|---|
| committer | Tejun Heo <tj@kernel.org> | 2014-02-22 15:35:42 -0500 |
| commit | 23b07d4cb3c0c850055cf968af44019b8da185fb (patch) | |
| tree | 0af2a4e9dfdaa99a566ca51198369b09704c14b7 | |
| parent | 96a01ba52c60fdd74dd6e8cf06645d06515b1396 (diff) | |
ahci-platform: "Library-ise" ahci_probe functionality
ahci_probe consists of 3 steps:
1) Get resources (get mmio, clks, regulator)
2) Enable resources, handled by ahci_platform_enable_resouces
3) The more or less standard ahci-host controller init sequence
This commit refactors step 1 and 3 into separate functions, so the platform
drivers for AHCI implementations which need a specific order in step 2,
and / or need to do some custom register poking at some time, can re-use
ahci-platform.c code without needing to copy and paste it.
Note that ahci_platform_init_host's prototype takes the 3 non function
members of ahci_platform_data as arguments, the idea is that drivers using
the new exported utility functions will not use ahci_platform_data at all,
and hopefully in the future ahci_platform_data can go away entirely.
tj: Minor comment formatting updates.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
| -rw-r--r-- | drivers/ata/ahci_platform.c | 188 | ||||
| -rw-r--r-- | include/linux/ahci_platform.h | 14 |
2 files changed, 137 insertions, 65 deletions
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index a32df31013cb..19e9eaafb1f2 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c | |||
| @@ -188,64 +188,60 @@ void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) | |||
| 188 | } | 188 | } |
| 189 | EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); | 189 | EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); |
| 190 | 190 | ||
| 191 | static void ahci_put_clks(struct ahci_host_priv *hpriv) | 191 | static void ahci_platform_put_resources(struct device *dev, void *res) |
| 192 | { | 192 | { |
| 193 | struct ahci_host_priv *hpriv = res; | ||
| 193 | int c; | 194 | int c; |
| 194 | 195 | ||
| 195 | for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) | 196 | for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) |
| 196 | clk_put(hpriv->clks[c]); | 197 | clk_put(hpriv->clks[c]); |
| 197 | } | 198 | } |
| 198 | 199 | ||
| 199 | static int ahci_probe(struct platform_device *pdev) | 200 | /** |
| 201 | * ahci_platform_get_resources - Get platform resources | ||
| 202 | * @pdev: platform device to get resources for | ||
| 203 | * | ||
| 204 | * This function allocates an ahci_host_priv struct, and gets the following | ||
| 205 | * resources, storing a reference to them inside the returned struct: | ||
| 206 | * | ||
| 207 | * 1) mmio registers (IORESOURCE_MEM 0, mandatory) | ||
| 208 | * 2) regulator for controlling the targets power (optional) | ||
| 209 | * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, | ||
| 210 | * or for non devicetree enabled platforms a single clock | ||
| 211 | * | ||
| 212 | * RETURNS: | ||
| 213 | * The allocated ahci_host_priv on success, otherwise an ERR_PTR value | ||
| 214 | */ | ||
| 215 | struct ahci_host_priv *ahci_platform_get_resources( | ||
| 216 | struct platform_device *pdev) | ||
| 200 | { | 217 | { |
| 201 | struct device *dev = &pdev->dev; | 218 | struct device *dev = &pdev->dev; |
| 202 | struct ahci_platform_data *pdata = dev_get_platdata(dev); | ||
| 203 | const struct platform_device_id *id = platform_get_device_id(pdev); | ||
| 204 | struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0]; | ||
| 205 | const struct ata_port_info *ppi[] = { &pi, NULL }; | ||
| 206 | struct ahci_host_priv *hpriv; | 219 | struct ahci_host_priv *hpriv; |
| 207 | struct ata_host *host; | ||
| 208 | struct resource *mem; | ||
| 209 | struct clk *clk; | 220 | struct clk *clk; |
| 210 | int irq; | 221 | int i, rc = -ENOMEM; |
| 211 | int n_ports; | ||
| 212 | int i; | ||
| 213 | int rc; | ||
| 214 | 222 | ||
| 215 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 223 | if (!devres_open_group(dev, NULL, GFP_KERNEL)) |
| 216 | if (!mem) { | 224 | return ERR_PTR(-ENOMEM); |
| 217 | dev_err(dev, "no mmio space\n"); | ||
| 218 | return -EINVAL; | ||
| 219 | } | ||
| 220 | |||
| 221 | irq = platform_get_irq(pdev, 0); | ||
| 222 | if (irq <= 0) { | ||
| 223 | dev_err(dev, "no irq\n"); | ||
| 224 | return -EINVAL; | ||
| 225 | } | ||
| 226 | 225 | ||
| 227 | if (pdata && pdata->ata_port_info) | 226 | hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), |
| 228 | pi = *pdata->ata_port_info; | 227 | GFP_KERNEL); |
| 229 | 228 | if (!hpriv) | |
| 230 | hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); | 229 | goto err_out; |
| 231 | if (!hpriv) { | ||
| 232 | dev_err(dev, "can't alloc ahci_host_priv\n"); | ||
| 233 | return -ENOMEM; | ||
| 234 | } | ||
| 235 | 230 | ||
| 236 | hpriv->flags |= (unsigned long)pi.private_data; | 231 | devres_add(dev, hpriv); |
| 237 | 232 | ||
| 238 | hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); | 233 | hpriv->mmio = devm_ioremap_resource(dev, |
| 234 | platform_get_resource(pdev, IORESOURCE_MEM, 0)); | ||
| 239 | if (!hpriv->mmio) { | 235 | if (!hpriv->mmio) { |
| 240 | dev_err(dev, "can't map %pR\n", mem); | 236 | dev_err(dev, "no mmio space\n"); |
| 241 | return -ENOMEM; | 237 | goto err_out; |
| 242 | } | 238 | } |
| 243 | 239 | ||
| 244 | hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); | 240 | hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); |
| 245 | if (IS_ERR(hpriv->target_pwr)) { | 241 | if (IS_ERR(hpriv->target_pwr)) { |
| 246 | rc = PTR_ERR(hpriv->target_pwr); | 242 | rc = PTR_ERR(hpriv->target_pwr); |
| 247 | if (rc == -EPROBE_DEFER) | 243 | if (rc == -EPROBE_DEFER) |
| 248 | return -EPROBE_DEFER; | 244 | goto err_out; |
| 249 | hpriv->target_pwr = NULL; | 245 | hpriv->target_pwr = NULL; |
| 250 | } | 246 | } |
| 251 | 247 | ||
| @@ -264,33 +260,59 @@ static int ahci_probe(struct platform_device *pdev) | |||
| 264 | if (IS_ERR(clk)) { | 260 | if (IS_ERR(clk)) { |
| 265 | rc = PTR_ERR(clk); | 261 | rc = PTR_ERR(clk); |
| 266 | if (rc == -EPROBE_DEFER) | 262 | if (rc == -EPROBE_DEFER) |
| 267 | goto free_clk; | 263 | goto err_out; |
| 268 | break; | 264 | break; |
| 269 | } | 265 | } |
| 270 | hpriv->clks[i] = clk; | 266 | hpriv->clks[i] = clk; |
| 271 | } | 267 | } |
| 272 | 268 | ||
| 273 | rc = ahci_platform_enable_resources(hpriv); | 269 | devres_remove_group(dev, NULL); |
| 274 | if (rc) | 270 | return hpriv; |
| 275 | goto free_clk; | ||
| 276 | 271 | ||
| 277 | /* | 272 | err_out: |
| 278 | * Some platforms might need to prepare for mmio region access, | 273 | devres_release_group(dev, NULL); |
| 279 | * which could be done in the following init call. So, the mmio | 274 | return ERR_PTR(rc); |
| 280 | * region shouldn't be accessed before init (if provided) has | 275 | } |
| 281 | * returned successfully. | 276 | EXPORT_SYMBOL_GPL(ahci_platform_get_resources); |
| 282 | */ | 277 | |
| 283 | if (pdata && pdata->init) { | 278 | /** |
| 284 | rc = pdata->init(dev, hpriv->mmio); | 279 | * ahci_platform_init_host - Bring up an ahci-platform host |
| 285 | if (rc) | 280 | * @pdev: platform device pointer for the host |
| 286 | goto disable_resources; | 281 | * @hpriv: ahci-host private data for the host |
| 287 | } | 282 | * @pi_template: template for the ata_port_info to use |
| 283 | * @force_port_map: param passed to ahci_save_initial_config | ||
| 284 | * @mask_port_map: param passed to ahci_save_initial_config | ||
| 285 | * | ||
| 286 | * This function does all the usual steps needed to bring up an | ||
| 287 | * ahci-platform host, note any necessary resources (ie clks, phy, etc.) | ||
| 288 | * must be initialized / enabled before calling this. | ||
| 289 | * | ||
| 290 | * RETURNS: | ||
| 291 | * 0 on success otherwise a negative error code | ||
| 292 | */ | ||
| 293 | int ahci_platform_init_host(struct platform_device *pdev, | ||
| 294 | struct ahci_host_priv *hpriv, | ||
| 295 | const struct ata_port_info *pi_template, | ||
| 296 | unsigned int force_port_map, | ||
| 297 | unsigned int mask_port_map) | ||
| 298 | { | ||
| 299 | struct device *dev = &pdev->dev; | ||
| 300 | struct ata_port_info pi = *pi_template; | ||
| 301 | const struct ata_port_info *ppi[] = { &pi, NULL }; | ||
| 302 | struct ata_host *host; | ||
| 303 | int i, irq, n_ports, rc; | ||
| 288 | 304 | ||
| 289 | ahci_save_initial_config(dev, hpriv, | 305 | irq = platform_get_irq(pdev, 0); |
| 290 | pdata ? pdata->force_port_map : 0, | 306 | if (irq <= 0) { |
| 291 | pdata ? pdata->mask_port_map : 0); | 307 | dev_err(dev, "no irq\n"); |
| 308 | return -EINVAL; | ||
| 309 | } | ||
| 292 | 310 | ||
| 293 | /* prepare host */ | 311 | /* prepare host */ |
| 312 | hpriv->flags |= (unsigned long)pi.private_data; | ||
| 313 | |||
| 314 | ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); | ||
| 315 | |||
| 294 | if (hpriv->cap & HOST_CAP_NCQ) | 316 | if (hpriv->cap & HOST_CAP_NCQ) |
| 295 | pi.flags |= ATA_FLAG_NCQ; | 317 | pi.flags |= ATA_FLAG_NCQ; |
| 296 | 318 | ||
| @@ -307,10 +329,8 @@ static int ahci_probe(struct platform_device *pdev) | |||
| 307 | n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); | 329 | n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); |
| 308 | 330 | ||
| 309 | host = ata_host_alloc_pinfo(dev, ppi, n_ports); | 331 | host = ata_host_alloc_pinfo(dev, ppi, n_ports); |
| 310 | if (!host) { | 332 | if (!host) |
| 311 | rc = -ENOMEM; | 333 | return -ENOMEM; |
| 312 | goto pdata_exit; | ||
| 313 | } | ||
| 314 | 334 | ||
| 315 | host->private_data = hpriv; | 335 | host->private_data = hpriv; |
| 316 | 336 | ||
| @@ -325,7 +345,8 @@ static int ahci_probe(struct platform_device *pdev) | |||
| 325 | for (i = 0; i < host->n_ports; i++) { | 345 | for (i = 0; i < host->n_ports; i++) { |
| 326 | struct ata_port *ap = host->ports[i]; | 346 | struct ata_port *ap = host->ports[i]; |
| 327 | 347 | ||
| 328 | ata_port_desc(ap, "mmio %pR", mem); | 348 | ata_port_desc(ap, "mmio %pR", |
| 349 | platform_get_resource(pdev, IORESOURCE_MEM, 0)); | ||
| 329 | ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); | 350 | ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); |
| 330 | 351 | ||
| 331 | /* set enclosure management message type */ | 352 | /* set enclosure management message type */ |
| @@ -339,13 +360,53 @@ static int ahci_probe(struct platform_device *pdev) | |||
| 339 | 360 | ||
| 340 | rc = ahci_reset_controller(host); | 361 | rc = ahci_reset_controller(host); |
| 341 | if (rc) | 362 | if (rc) |
| 342 | goto pdata_exit; | 363 | return rc; |
| 343 | 364 | ||
| 344 | ahci_init_controller(host); | 365 | ahci_init_controller(host); |
| 345 | ahci_print_info(host, "platform"); | 366 | ahci_print_info(host, "platform"); |
| 346 | 367 | ||
| 347 | rc = ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, | 368 | return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, |
| 348 | &ahci_platform_sht); | 369 | &ahci_platform_sht); |
| 370 | } | ||
| 371 | EXPORT_SYMBOL_GPL(ahci_platform_init_host); | ||
| 372 | |||
| 373 | static int ahci_probe(struct platform_device *pdev) | ||
| 374 | { | ||
| 375 | struct device *dev = &pdev->dev; | ||
| 376 | struct ahci_platform_data *pdata = dev_get_platdata(dev); | ||
| 377 | const struct platform_device_id *id = platform_get_device_id(pdev); | ||
| 378 | const struct ata_port_info *pi_template; | ||
| 379 | struct ahci_host_priv *hpriv; | ||
| 380 | int rc; | ||
| 381 | |||
| 382 | hpriv = ahci_platform_get_resources(pdev); | ||
| 383 | if (IS_ERR(hpriv)) | ||
| 384 | return PTR_ERR(hpriv); | ||
| 385 | |||
| 386 | rc = ahci_platform_enable_resources(hpriv); | ||
| 387 | if (rc) | ||
| 388 | return rc; | ||
| 389 | |||
| 390 | /* | ||
| 391 | * Some platforms might need to prepare for mmio region access, | ||
| 392 | * which could be done in the following init call. So, the mmio | ||
| 393 | * region shouldn't be accessed before init (if provided) has | ||
| 394 | * returned successfully. | ||
| 395 | */ | ||
| 396 | if (pdata && pdata->init) { | ||
| 397 | rc = pdata->init(dev, hpriv->mmio); | ||
| 398 | if (rc) | ||
| 399 | goto disable_resources; | ||
| 400 | } | ||
| 401 | |||
| 402 | if (pdata && pdata->ata_port_info) | ||
| 403 | pi_template = pdata->ata_port_info; | ||
| 404 | else | ||
| 405 | pi_template = &ahci_port_info[id ? id->driver_data : 0]; | ||
| 406 | |||
| 407 | rc = ahci_platform_init_host(pdev, hpriv, pi_template, | ||
| 408 | pdata ? pdata->force_port_map : 0, | ||
| 409 | pdata ? pdata->mask_port_map : 0); | ||
| 349 | if (rc) | 410 | if (rc) |
| 350 | goto pdata_exit; | 411 | goto pdata_exit; |
| 351 | 412 | ||
| @@ -355,8 +416,6 @@ pdata_exit: | |||
| 355 | pdata->exit(dev); | 416 | pdata->exit(dev); |
| 356 | disable_resources: | 417 | disable_resources: |
| 357 | ahci_platform_disable_resources(hpriv); | 418 | ahci_platform_disable_resources(hpriv); |
| 358 | free_clk: | ||
| 359 | ahci_put_clks(hpriv); | ||
| 360 | return rc; | 419 | return rc; |
| 361 | } | 420 | } |
| 362 | 421 | ||
| @@ -370,7 +429,6 @@ static void ahci_host_stop(struct ata_host *host) | |||
| 370 | pdata->exit(dev); | 429 | pdata->exit(dev); |
| 371 | 430 | ||
| 372 | ahci_platform_disable_resources(hpriv); | 431 | ahci_platform_disable_resources(hpriv); |
| 373 | ahci_put_clks(hpriv); | ||
| 374 | } | 432 | } |
| 375 | 433 | ||
| 376 | #ifdef CONFIG_PM_SLEEP | 434 | #ifdef CONFIG_PM_SLEEP |
diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h index b674b01ce9bc..b80c51c20f76 100644 --- a/include/linux/ahci_platform.h +++ b/include/linux/ahci_platform.h | |||
| @@ -20,7 +20,14 @@ | |||
| 20 | struct device; | 20 | struct device; |
| 21 | struct ata_port_info; | 21 | struct ata_port_info; |
| 22 | struct ahci_host_priv; | 22 | struct ahci_host_priv; |
| 23 | struct platform_device; | ||
| 23 | 24 | ||
| 25 | /* | ||
| 26 | * Note ahci_platform_data is deprecated, it is only kept around for use | ||
| 27 | * by the old da850 and spear13xx ahci code. | ||
| 28 | * New drivers should instead declare their own platform_driver struct, and | ||
| 29 | * use ahci_platform* functions in their own probe, suspend and resume methods. | ||
| 30 | */ | ||
| 24 | struct ahci_platform_data { | 31 | struct ahci_platform_data { |
| 25 | int (*init)(struct device *dev, void __iomem *addr); | 32 | int (*init)(struct device *dev, void __iomem *addr); |
| 26 | void (*exit)(struct device *dev); | 33 | void (*exit)(struct device *dev); |
| @@ -35,5 +42,12 @@ int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); | |||
| 35 | void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); | 42 | void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); |
| 36 | int ahci_platform_enable_resources(struct ahci_host_priv *hpriv); | 43 | int ahci_platform_enable_resources(struct ahci_host_priv *hpriv); |
| 37 | void ahci_platform_disable_resources(struct ahci_host_priv *hpriv); | 44 | void ahci_platform_disable_resources(struct ahci_host_priv *hpriv); |
| 45 | struct ahci_host_priv *ahci_platform_get_resources( | ||
| 46 | struct platform_device *pdev); | ||
| 47 | int ahci_platform_init_host(struct platform_device *pdev, | ||
| 48 | struct ahci_host_priv *hpriv, | ||
| 49 | const struct ata_port_info *pi_template, | ||
| 50 | unsigned int force_port_map, | ||
| 51 | unsigned int mask_port_map); | ||
| 38 | 52 | ||
| 39 | #endif /* _AHCI_PLATFORM_H */ | 53 | #endif /* _AHCI_PLATFORM_H */ |
