diff options
| author | Robin Murphy <robin.murphy@arm.com> | 2016-09-12 12:13:55 -0400 |
|---|---|---|
| committer | Will Deacon <will.deacon@arm.com> | 2016-09-16 04:34:20 -0400 |
| commit | adfec2e709d2a48dbd756d65fe4fa8e4aae529a3 (patch) | |
| tree | e77fc536a77f4aa7c658b9c34b8c177b3e7ecefa /drivers/iommu | |
| parent | 588888a7399db352d2b1a41c9d5b3bf0fd482390 (diff) | |
iommu/arm-smmu: Convert to iommu_fwspec
In the final step of preparation for full generic configuration support,
swap our fixed-size master_cfg for the generic iommu_fwspec. For the
legacy DT bindings, the driver simply gets to act as its own 'firmware'.
Farewell, arbitrary MAX_MASTER_STREAMIDS!
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'drivers/iommu')
| -rw-r--r-- | drivers/iommu/arm-smmu.c | 140 |
1 files changed, 78 insertions, 62 deletions
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 7a2bd60a54da..9dbb6a37e625 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c | |||
| @@ -42,6 +42,7 @@ | |||
| 42 | #include <linux/of.h> | 42 | #include <linux/of.h> |
| 43 | #include <linux/of_address.h> | 43 | #include <linux/of_address.h> |
| 44 | #include <linux/of_device.h> | 44 | #include <linux/of_device.h> |
| 45 | #include <linux/of_iommu.h> | ||
| 45 | #include <linux/pci.h> | 46 | #include <linux/pci.h> |
| 46 | #include <linux/platform_device.h> | 47 | #include <linux/platform_device.h> |
| 47 | #include <linux/slab.h> | 48 | #include <linux/slab.h> |
| @@ -51,9 +52,6 @@ | |||
| 51 | 52 | ||
| 52 | #include "io-pgtable.h" | 53 | #include "io-pgtable.h" |
| 53 | 54 | ||
| 54 | /* Maximum number of stream IDs assigned to a single device */ | ||
| 55 | #define MAX_MASTER_STREAMIDS 128 | ||
| 56 | |||
| 57 | /* Maximum number of context banks per SMMU */ | 55 | /* Maximum number of context banks per SMMU */ |
| 58 | #define ARM_SMMU_MAX_CBS 128 | 56 | #define ARM_SMMU_MAX_CBS 128 |
| 59 | 57 | ||
| @@ -321,13 +319,13 @@ struct arm_smmu_smr { | |||
| 321 | 319 | ||
| 322 | struct arm_smmu_master_cfg { | 320 | struct arm_smmu_master_cfg { |
| 323 | struct arm_smmu_device *smmu; | 321 | struct arm_smmu_device *smmu; |
| 324 | int num_streamids; | 322 | s16 smendx[]; |
| 325 | u16 streamids[MAX_MASTER_STREAMIDS]; | ||
| 326 | s16 smendx[MAX_MASTER_STREAMIDS]; | ||
| 327 | }; | 323 | }; |
| 328 | #define INVALID_SMENDX -1 | 324 | #define INVALID_SMENDX -1 |
| 329 | #define for_each_cfg_sme(cfg, i, idx) \ | 325 | #define __fwspec_cfg(fw) ((struct arm_smmu_master_cfg *)fw->iommu_priv) |
| 330 | for (i = 0; idx = cfg->smendx[i], i < cfg->num_streamids; ++i) | 326 | #define fwspec_smmu(fw) (__fwspec_cfg(fw)->smmu) |
| 327 | #define for_each_cfg_sme(fw, i, idx) \ | ||
| 328 | for (i = 0; idx = __fwspec_cfg(fw)->smendx[i], i < fw->num_ids; ++i) | ||
| 331 | 329 | ||
| 332 | struct arm_smmu_device { | 330 | struct arm_smmu_device { |
| 333 | struct device *dev; | 331 | struct device *dev; |
| @@ -480,14 +478,16 @@ static int __find_legacy_master_phandle(struct device *dev, void *data) | |||
| 480 | } | 478 | } |
| 481 | 479 | ||
| 482 | static struct platform_driver arm_smmu_driver; | 480 | static struct platform_driver arm_smmu_driver; |
| 481 | static struct iommu_ops arm_smmu_ops; | ||
| 483 | 482 | ||
| 484 | static int arm_smmu_register_legacy_master(struct device *dev) | 483 | static int arm_smmu_register_legacy_master(struct device *dev, |
| 484 | struct arm_smmu_device **smmu) | ||
| 485 | { | 485 | { |
| 486 | struct arm_smmu_device *smmu; | 486 | struct device *smmu_dev; |
| 487 | struct arm_smmu_master_cfg *cfg; | ||
| 488 | struct device_node *np; | 487 | struct device_node *np; |
| 489 | struct of_phandle_iterator it; | 488 | struct of_phandle_iterator it; |
| 490 | void *data = ⁢ | 489 | void *data = ⁢ |
| 490 | u32 *sids; | ||
| 491 | __be32 pci_sid; | 491 | __be32 pci_sid; |
| 492 | int err; | 492 | int err; |
| 493 | 493 | ||
| @@ -500,20 +500,13 @@ static int arm_smmu_register_legacy_master(struct device *dev) | |||
| 500 | it.node = np; | 500 | it.node = np; |
| 501 | err = driver_for_each_device(&arm_smmu_driver.driver, NULL, &data, | 501 | err = driver_for_each_device(&arm_smmu_driver.driver, NULL, &data, |
| 502 | __find_legacy_master_phandle); | 502 | __find_legacy_master_phandle); |
| 503 | smmu_dev = data; | ||
| 503 | of_node_put(np); | 504 | of_node_put(np); |
| 504 | if (err == 0) | 505 | if (err == 0) |
| 505 | return -ENODEV; | 506 | return -ENODEV; |
| 506 | if (err < 0) | 507 | if (err < 0) |
| 507 | return err; | 508 | return err; |
| 508 | 509 | ||
| 509 | smmu = dev_get_drvdata(data); | ||
| 510 | |||
| 511 | if (it.cur_count > MAX_MASTER_STREAMIDS) { | ||
| 512 | dev_err(smmu->dev, | ||
| 513 | "reached maximum number (%d) of stream IDs for master device %s\n", | ||
| 514 | MAX_MASTER_STREAMIDS, dev_name(dev)); | ||
| 515 | return -ENOSPC; | ||
| 516 | } | ||
| 517 | if (dev_is_pci(dev)) { | 510 | if (dev_is_pci(dev)) { |
| 518 | /* "mmu-masters" assumes Stream ID == Requester ID */ | 511 | /* "mmu-masters" assumes Stream ID == Requester ID */ |
| 519 | pci_for_each_dma_alias(to_pci_dev(dev), __arm_smmu_get_pci_sid, | 512 | pci_for_each_dma_alias(to_pci_dev(dev), __arm_smmu_get_pci_sid, |
| @@ -522,17 +515,20 @@ static int arm_smmu_register_legacy_master(struct device *dev) | |||
| 522 | it.cur_count = 1; | 515 | it.cur_count = 1; |
| 523 | } | 516 | } |
| 524 | 517 | ||
| 525 | cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); | 518 | err = iommu_fwspec_init(dev, &smmu_dev->of_node->fwnode, |
| 526 | if (!cfg) | 519 | &arm_smmu_ops); |
| 527 | return -ENOMEM; | 520 | if (err) |
| 528 | 521 | return err; | |
| 529 | cfg->smmu = smmu; | ||
| 530 | dev->archdata.iommu = cfg; | ||
| 531 | 522 | ||
| 532 | while (it.cur_count--) | 523 | sids = kcalloc(it.cur_count, sizeof(*sids), GFP_KERNEL); |
| 533 | cfg->streamids[cfg->num_streamids++] = be32_to_cpup(it.cur++); | 524 | if (!sids) |
| 525 | return -ENOMEM; | ||
| 534 | 526 | ||
| 535 | return 0; | 527 | *smmu = dev_get_drvdata(smmu_dev); |
| 528 | of_phandle_iterator_args(&it, sids, it.cur_count); | ||
| 529 | err = iommu_fwspec_add_ids(dev, sids, it.cur_count); | ||
| 530 | kfree(sids); | ||
| 531 | return err; | ||
| 536 | } | 532 | } |
| 537 | 533 | ||
| 538 | static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end) | 534 | static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end) |
| @@ -1127,7 +1123,8 @@ static bool arm_smmu_free_sme(struct arm_smmu_device *smmu, int idx) | |||
| 1127 | 1123 | ||
| 1128 | static int arm_smmu_master_alloc_smes(struct device *dev) | 1124 | static int arm_smmu_master_alloc_smes(struct device *dev) |
| 1129 | { | 1125 | { |
| 1130 | struct arm_smmu_master_cfg *cfg = dev->archdata.iommu; | 1126 | struct iommu_fwspec *fwspec = dev->iommu_fwspec; |
| 1127 | struct arm_smmu_master_cfg *cfg = fwspec->iommu_priv; | ||
| 1131 | struct arm_smmu_device *smmu = cfg->smmu; | 1128 | struct arm_smmu_device *smmu = cfg->smmu; |
| 1132 | struct arm_smmu_smr *smrs = smmu->smrs; | 1129 | struct arm_smmu_smr *smrs = smmu->smrs; |
| 1133 | struct iommu_group *group; | 1130 | struct iommu_group *group; |
| @@ -1135,19 +1132,19 @@ static int arm_smmu_master_alloc_smes(struct device *dev) | |||
| 1135 | 1132 | ||
| 1136 | mutex_lock(&smmu->stream_map_mutex); | 1133 | mutex_lock(&smmu->stream_map_mutex); |
| 1137 | /* Figure out a viable stream map entry allocation */ | 1134 | /* Figure out a viable stream map entry allocation */ |
| 1138 | for_each_cfg_sme(cfg, i, idx) { | 1135 | for_each_cfg_sme(fwspec, i, idx) { |
| 1139 | if (idx != INVALID_SMENDX) { | 1136 | if (idx != INVALID_SMENDX) { |
| 1140 | ret = -EEXIST; | 1137 | ret = -EEXIST; |
| 1141 | goto out_err; | 1138 | goto out_err; |
| 1142 | } | 1139 | } |
| 1143 | 1140 | ||
| 1144 | ret = arm_smmu_find_sme(smmu, cfg->streamids[i], 0); | 1141 | ret = arm_smmu_find_sme(smmu, fwspec->ids[i], 0); |
| 1145 | if (ret < 0) | 1142 | if (ret < 0) |
| 1146 | goto out_err; | 1143 | goto out_err; |
| 1147 | 1144 | ||
| 1148 | idx = ret; | 1145 | idx = ret; |
| 1149 | if (smrs && smmu->s2crs[idx].count == 0) { | 1146 | if (smrs && smmu->s2crs[idx].count == 0) { |
| 1150 | smrs[idx].id = cfg->streamids[i]; | 1147 | smrs[idx].id = fwspec->ids[i]; |
| 1151 | smrs[idx].mask = 0; /* We don't currently share SMRs */ | 1148 | smrs[idx].mask = 0; /* We don't currently share SMRs */ |
| 1152 | smrs[idx].valid = true; | 1149 | smrs[idx].valid = true; |
| 1153 | } | 1150 | } |
| @@ -1165,7 +1162,7 @@ static int arm_smmu_master_alloc_smes(struct device *dev) | |||
| 1165 | iommu_group_put(group); | 1162 | iommu_group_put(group); |
| 1166 | 1163 | ||
| 1167 | /* It worked! Now, poke the actual hardware */ | 1164 | /* It worked! Now, poke the actual hardware */ |
| 1168 | for_each_cfg_sme(cfg, i, idx) { | 1165 | for_each_cfg_sme(fwspec, i, idx) { |
| 1169 | arm_smmu_write_sme(smmu, idx); | 1166 | arm_smmu_write_sme(smmu, idx); |
| 1170 | smmu->s2crs[idx].group = group; | 1167 | smmu->s2crs[idx].group = group; |
| 1171 | } | 1168 | } |
| @@ -1182,13 +1179,14 @@ out_err: | |||
| 1182 | return ret; | 1179 | return ret; |
| 1183 | } | 1180 | } |
| 1184 | 1181 | ||
| 1185 | static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg) | 1182 | static void arm_smmu_master_free_smes(struct iommu_fwspec *fwspec) |
| 1186 | { | 1183 | { |
| 1187 | struct arm_smmu_device *smmu = cfg->smmu; | 1184 | struct arm_smmu_device *smmu = fwspec_smmu(fwspec); |
| 1185 | struct arm_smmu_master_cfg *cfg = fwspec->iommu_priv; | ||
| 1188 | int i, idx; | 1186 | int i, idx; |
| 1189 | 1187 | ||
| 1190 | mutex_lock(&smmu->stream_map_mutex); | 1188 | mutex_lock(&smmu->stream_map_mutex); |
| 1191 | for_each_cfg_sme(cfg, i, idx) { | 1189 | for_each_cfg_sme(fwspec, i, idx) { |
| 1192 | if (arm_smmu_free_sme(smmu, idx)) | 1190 | if (arm_smmu_free_sme(smmu, idx)) |
| 1193 | arm_smmu_write_sme(smmu, idx); | 1191 | arm_smmu_write_sme(smmu, idx); |
| 1194 | cfg->smendx[i] = INVALID_SMENDX; | 1192 | cfg->smendx[i] = INVALID_SMENDX; |
| @@ -1197,7 +1195,7 @@ static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg) | |||
