diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/cpufreq/powernv-cpufreq.c | 18 | ||||
| -rw-r--r-- | drivers/cpuidle/cpuidle-powernv.c | 16 | ||||
| -rw-r--r-- | drivers/memory/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/memory/Makefile | 1 | ||||
| -rw-r--r-- | drivers/memory/fsl-corenet-cf.c | 251 | ||||
| -rw-r--r-- | drivers/net/ethernet/freescale/fs_enet/mac-fec.c | 1 | ||||
| -rw-r--r-- | drivers/net/ethernet/freescale/fs_enet/mac-scc.c | 1 | ||||
| -rw-r--r-- | drivers/pcmcia/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/pcmcia/Makefile | 1 | ||||
| -rw-r--r-- | drivers/pcmcia/m8xx_pcmcia.c | 1168 | ||||
| -rw-r--r-- | drivers/vfio/Makefile | 1 | ||||
| -rw-r--r-- | drivers/vfio/pci/vfio_pci.c | 18 | ||||
| -rw-r--r-- | drivers/vfio/vfio_iommu_spapr_tce.c | 17 | ||||
| -rw-r--r-- | drivers/vfio/vfio_spapr_eeh.c | 87 |
14 files changed, 404 insertions, 1196 deletions
diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c index bb1d08dc8cc8..379c0837f5a9 100644 --- a/drivers/cpufreq/powernv-cpufreq.c +++ b/drivers/cpufreq/powernv-cpufreq.c | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | #include <linux/of.h> | 28 | #include <linux/of.h> |
| 29 | 29 | ||
| 30 | #include <asm/cputhreads.h> | 30 | #include <asm/cputhreads.h> |
| 31 | #include <asm/firmware.h> | ||
| 31 | #include <asm/reg.h> | 32 | #include <asm/reg.h> |
| 32 | #include <asm/smp.h> /* Required for cpu_sibling_mask() in UP configs */ | 33 | #include <asm/smp.h> /* Required for cpu_sibling_mask() in UP configs */ |
| 33 | 34 | ||
| @@ -98,7 +99,11 @@ static int init_powernv_pstates(void) | |||
| 98 | return -ENODEV; | 99 | return -ENODEV; |
| 99 | } | 100 | } |
| 100 | 101 | ||
| 101 | WARN_ON(len_ids != len_freqs); | 102 | if (len_ids != len_freqs) { |
| 103 | pr_warn("Entries in ibm,pstate-ids and " | ||
| 104 | "ibm,pstate-frequencies-mhz does not match\n"); | ||
| 105 | } | ||
| 106 | |||
| 102 | nr_pstates = min(len_ids, len_freqs) / sizeof(u32); | 107 | nr_pstates = min(len_ids, len_freqs) / sizeof(u32); |
| 103 | if (!nr_pstates) { | 108 | if (!nr_pstates) { |
| 104 | pr_warn("No PStates found\n"); | 109 | pr_warn("No PStates found\n"); |
| @@ -131,7 +136,12 @@ static unsigned int pstate_id_to_freq(int pstate_id) | |||
| 131 | int i; | 136 | int i; |
| 132 | 137 | ||
| 133 | i = powernv_pstate_info.max - pstate_id; | 138 | i = powernv_pstate_info.max - pstate_id; |
| 134 | BUG_ON(i >= powernv_pstate_info.nr_pstates || i < 0); | 139 | if (i >= powernv_pstate_info.nr_pstates || i < 0) { |
| 140 | pr_warn("PState id %d outside of PState table, " | ||
| 141 | "reporting nominal id %d instead\n", | ||
| 142 | pstate_id, powernv_pstate_info.nominal); | ||
| 143 | i = powernv_pstate_info.max - powernv_pstate_info.nominal; | ||
| 144 | } | ||
| 135 | 145 | ||
| 136 | return powernv_freqs[i].frequency; | 146 | return powernv_freqs[i].frequency; |
| 137 | } | 147 | } |
| @@ -321,6 +331,10 @@ static int __init powernv_cpufreq_init(void) | |||
| 321 | { | 331 | { |
| 322 | int rc = 0; | 332 | int rc = 0; |
| 323 | 333 | ||
| 334 | /* Don't probe on pseries (guest) platforms */ | ||
| 335 | if (!firmware_has_feature(FW_FEATURE_OPALv3)) | ||
| 336 | return -ENODEV; | ||
| 337 | |||
| 324 | /* Discover pstates from device tree and init */ | 338 | /* Discover pstates from device tree and init */ |
| 325 | rc = init_powernv_pstates(); | 339 | rc = init_powernv_pstates(); |
| 326 | if (rc) { | 340 | if (rc) { |
diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c index 74f5788d50b1..a64be578dab2 100644 --- a/drivers/cpuidle/cpuidle-powernv.c +++ b/drivers/cpuidle/cpuidle-powernv.c | |||
| @@ -160,10 +160,10 @@ static int powernv_cpuidle_driver_init(void) | |||
| 160 | static int powernv_add_idle_states(void) | 160 | static int powernv_add_idle_states(void) |
| 161 | { | 161 | { |
| 162 | struct device_node *power_mgt; | 162 | struct device_node *power_mgt; |
| 163 | struct property *prop; | ||
| 164 | int nr_idle_states = 1; /* Snooze */ | 163 | int nr_idle_states = 1; /* Snooze */ |
| 165 | int dt_idle_states; | 164 | int dt_idle_states; |
| 166 | u32 *flags; | 165 | const __be32 *idle_state_flags; |
| 166 | u32 len_flags, flags; | ||
| 167 | int i; | 167 | int i; |
| 168 | 168 | ||
| 169 | /* Currently we have snooze statically defined */ | 169 | /* Currently we have snooze statically defined */ |
| @@ -174,18 +174,18 @@ static int powernv_add_idle_states(void) | |||
| 174 | return nr_idle_states; | 174 | return nr_idle_states; |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | prop = of_find_property(power_mgt, "ibm,cpu-idle-state-flags", NULL); | 177 | idle_state_flags = of_get_property(power_mgt, "ibm,cpu-idle-state-flags", &len_flags); |
| 178 | if (!prop) { | 178 | if (!idle_state_flags) { |
| 179 | pr_warn("DT-PowerMgmt: missing ibm,cpu-idle-state-flags\n"); | 179 | pr_warn("DT-PowerMgmt: missing ibm,cpu-idle-state-flags\n"); |
| 180 | return nr_idle_states; | 180 | return nr_idle_states; |
| 181 | } | 181 | } |
| 182 | 182 | ||
| 183 | dt_idle_states = prop->length / sizeof(u32); | 183 | dt_idle_states = len_flags / sizeof(u32); |
| 184 | flags = (u32 *) prop->value; | ||
| 185 | 184 | ||
| 186 | for (i = 0; i < dt_idle_states; i++) { | 185 | for (i = 0; i < dt_idle_states; i++) { |
| 187 | 186 | ||
| 188 | if (flags[i] & IDLE_USE_INST_NAP) { | 187 | flags = be32_to_cpu(idle_state_flags[i]); |
| 188 | if (flags & IDLE_USE_INST_NAP) { | ||
| 189 | /* Add NAP state */ | 189 | /* Add NAP state */ |
| 190 | strcpy(powernv_states[nr_idle_states].name, "Nap"); | 190 | strcpy(powernv_states[nr_idle_states].name, "Nap"); |
| 191 | strcpy(powernv_states[nr_idle_states].desc, "Nap"); | 191 | strcpy(powernv_states[nr_idle_states].desc, "Nap"); |
| @@ -196,7 +196,7 @@ static int powernv_add_idle_states(void) | |||
| 196 | nr_idle_states++; | 196 | nr_idle_states++; |
| 197 | } | 197 | } |
| 198 | 198 | ||
| 199 | if (flags[i] & IDLE_USE_INST_SLEEP) { | 199 | if (flags & IDLE_USE_INST_SLEEP) { |
| 200 | /* Add FASTSLEEP state */ | 200 | /* Add FASTSLEEP state */ |
| 201 | strcpy(powernv_states[nr_idle_states].name, "FastSleep"); | 201 | strcpy(powernv_states[nr_idle_states].name, "FastSleep"); |
| 202 | strcpy(powernv_states[nr_idle_states].desc, "FastSleep"); | 202 | strcpy(powernv_states[nr_idle_states].desc, "FastSleep"); |
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index c59e9c96e86d..fab81a143bd7 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig | |||
| @@ -61,6 +61,16 @@ config TEGRA30_MC | |||
| 61 | analysis, especially for IOMMU/SMMU(System Memory Management | 61 | analysis, especially for IOMMU/SMMU(System Memory Management |
| 62 | Unit) module. | 62 | Unit) module. |
| 63 | 63 | ||
| 64 | config FSL_CORENET_CF | ||
| 65 | tristate "Freescale CoreNet Error Reporting" | ||
| 66 | depends on FSL_SOC_BOOKE | ||
| 67 | help | ||
| 68 | Say Y for reporting of errors from the Freescale CoreNet | ||
| 69 | Coherency Fabric. Errors reported include accesses to | ||
| 70 | physical addresses that mapped by no local access window | ||
| 71 | (LAW) or an invalid LAW, as well as bad cache state that | ||
| 72 | represents a coherency violation. | ||
| 73 | |||
| 64 | config FSL_IFC | 74 | config FSL_IFC |
| 65 | bool | 75 | bool |
| 66 | depends on FSL_SOC | 76 | depends on FSL_SOC |
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index 71160a2b7313..4055c47f45ab 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile | |||
| @@ -7,6 +7,7 @@ obj-$(CONFIG_OF) += of_memory.o | |||
| 7 | endif | 7 | endif |
| 8 | obj-$(CONFIG_TI_AEMIF) += ti-aemif.o | 8 | obj-$(CONFIG_TI_AEMIF) += ti-aemif.o |
| 9 | obj-$(CONFIG_TI_EMIF) += emif.o | 9 | obj-$(CONFIG_TI_EMIF) += emif.o |
| 10 | obj-$(CONFIG_FSL_CORENET_CF) += fsl-corenet-cf.o | ||
| 10 | obj-$(CONFIG_FSL_IFC) += fsl_ifc.o | 11 | obj-$(CONFIG_FSL_IFC) += fsl_ifc.o |
| 11 | obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o | 12 | obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o |
| 12 | obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o | 13 | obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o |
diff --git a/drivers/memory/fsl-corenet-cf.c b/drivers/memory/fsl-corenet-cf.c new file mode 100644 index 000000000000..c9443fc136db --- /dev/null +++ b/drivers/memory/fsl-corenet-cf.c | |||
| @@ -0,0 +1,251 @@ | |||
| 1 | /* | ||
| 2 | * CoreNet Coherency Fabric error reporting | ||
| 3 | * | ||
| 4 | * Copyright 2014 Freescale Semiconductor Inc. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License as published by the | ||
| 8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 9 | * option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/interrupt.h> | ||
| 13 | #include <linux/io.h> | ||
| 14 | #include <linux/irq.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/of.h> | ||
| 17 | #include <linux/of_address.h> | ||
| 18 | #include <linux/of_device.h> | ||
| 19 | #include <linux/of_irq.h> | ||
| 20 | #include <linux/platform_device.h> | ||
| 21 | |||
| 22 | enum ccf_version { | ||
| 23 | CCF1, | ||
| 24 | CCF2, | ||
| 25 | }; | ||
| 26 | |||
| 27 | struct ccf_info { | ||
| 28 | enum ccf_version version; | ||
| 29 | int err_reg_offs; | ||
| 30 | }; | ||
| 31 | |||
| 32 | static const struct ccf_info ccf1_info = { | ||
| 33 | .version = CCF1, | ||
| 34 | .err_reg_offs = 0xa00, | ||
| 35 | }; | ||
| 36 | |||
| 37 | static const struct ccf_info ccf2_info = { | ||
| 38 | .version = CCF2, | ||
| 39 | .err_reg_offs = 0xe40, | ||
| 40 | }; | ||
| 41 | |||
| 42 | static const struct of_device_id ccf_matches[] = { | ||
| 43 | { | ||
| 44 | .compatible = "fsl,corenet1-cf", | ||
| 45 | .data = &ccf1_info, | ||
| 46 | }, | ||
| 47 | { | ||
| 48 | .compatible = "fsl,corenet2-cf", | ||
| 49 | .data = &ccf2_info, | ||
| 50 | }, | ||
| 51 | {} | ||
| 52 | }; | ||
| 53 | |||
| 54 | struct ccf_err_regs { | ||
| 55 | u32 errdet; /* 0x00 Error Detect Register */ | ||
| 56 | /* 0x04 Error Enable (ccf1)/Disable (ccf2) Register */ | ||
| 57 | u32 errdis; | ||
| 58 | /* 0x08 Error Interrupt Enable Register (ccf2 only) */ | ||
| 59 | u32 errinten; | ||
| 60 | u32 cecar; /* 0x0c Error Capture Attribute Register */ | ||
| 61 | u32 cecaddrh; /* 0x10 Error Capture Address High */ | ||
| 62 | u32 cecaddrl; /* 0x14 Error Capture Address Low */ | ||
| 63 | u32 cecar2; /* 0x18 Error Capture Attribute Register 2 */ | ||
| 64 | }; | ||
| 65 | |||
| 66 | /* LAE/CV also valid for errdis and errinten */ | ||
| 67 | #define ERRDET_LAE (1 << 0) /* Local Access Error */ | ||
| 68 | #define ERRDET_CV (1 << 1) /* Coherency Violation */ | ||
| 69 | #define ERRDET_CTYPE_SHIFT 26 /* Capture Type (ccf2 only) */ | ||
| 70 | #define ERRDET_CTYPE_MASK (0x1f << ERRDET_CTYPE_SHIFT) | ||
| 71 | #define ERRDET_CAP (1 << 31) /* Capture Valid (ccf2 only) */ | ||
| 72 | |||
| 73 | #define CECAR_VAL (1 << 0) /* Valid (ccf1 only) */ | ||
| 74 | #define CECAR_UVT (1 << 15) /* Unavailable target ID (ccf1) */ | ||
| 75 | #define CECAR_SRCID_SHIFT_CCF1 24 | ||
| 76 | #define CECAR_SRCID_MASK_CCF1 (0xff << CECAR_SRCID_SHIFT_CCF1) | ||
| 77 | #define CECAR_SRCID_SHIFT_CCF2 18 | ||
| 78 | #define CECAR_SRCID_MASK_CCF2 (0xff << CECAR_SRCID_SHIFT_CCF2) | ||
| 79 | |||
| 80 | #define CECADDRH_ADDRH 0xff | ||
| 81 | |||
| 82 | struct ccf_private { | ||
| 83 | const struct ccf_info *info; | ||
| 84 | struct device *dev; | ||
| 85 | void __iomem *regs; | ||
| 86 | struct ccf_err_regs __iomem *err_regs; | ||
| 87 | }; | ||
| 88 | |||
| 89 | static irqreturn_t ccf_irq(int irq, void *dev_id) | ||
| 90 | { | ||
| 91 | struct ccf_private *ccf = dev_id; | ||
| 92 | static DEFINE_RATELIMIT_STATE(ratelimit, DEFAULT_RATELIMIT_INTERVAL, | ||
| 93 | DEFAULT_RATELIMIT_BURST); | ||
| 94 | u32 errdet, cecar, cecar2; | ||
| 95 | u64 addr; | ||
| 96 | u32 src_id; | ||
| 97 | bool uvt = false; | ||
| 98 | bool cap_valid = false; | ||
| 99 | |||
| 100 | errdet = ioread32be(&ccf->err_regs->errdet); | ||
| 101 | cecar = ioread32be(&ccf->err_regs->cecar); | ||
| 102 | cecar2 = ioread32be(&ccf->err_regs->cecar2); | ||
| 103 | addr = ioread32be(&ccf->err_regs->cecaddrl); | ||
| 104 | addr |= ((u64)(ioread32be(&ccf->err_regs->cecaddrh) & | ||
| 105 | CECADDRH_ADDRH)) << 32; | ||
| 106 | |||
| 107 | if (!__ratelimit(&ratelimit)) | ||
| 108 | goto out; | ||
| 109 | |||
| 110 | switch (ccf->info->version) { | ||
| 111 | case CCF1: | ||
| 112 | if (cecar & CECAR_VAL) { | ||
| 113 | if (cecar & CECAR_UVT) | ||
| 114 | uvt = true; | ||
| 115 | |||
| 116 | src_id = (cecar & CECAR_SRCID_MASK_CCF1) >> | ||
| 117 | CECAR_SRCID_SHIFT_CCF1; | ||
| 118 | cap_valid = true; | ||
| 119 | } | ||
| 120 | |||
| 121 | break; | ||
| 122 | case CCF2: | ||
| 123 | if (errdet & ERRDET_CAP) { | ||
| 124 | src_id = (cecar & CECAR_SRCID_MASK_CCF2) >> | ||
| 125 | CECAR_SRCID_SHIFT_CCF2; | ||
| 126 | cap_valid = true; | ||
| 127 | } | ||
| 128 | |||
| 129 | break; | ||
| 130 | } | ||
| 131 | |||
| 132 | dev_crit(ccf->dev, "errdet 0x%08x cecar 0x%08x cecar2 0x%08x\n", | ||
| 133 | errdet, cecar, cecar2); | ||
| 134 | |||
| 135 | if (errdet & ERRDET_LAE) { | ||
| 136 | if (uvt) | ||
| 137 | dev_crit(ccf->dev, "LAW Unavailable Target ID\n"); | ||
| 138 | else | ||
| 139 | dev_crit(ccf->dev, "Local Access Window Error\n"); | ||
| 140 | } | ||
| 141 | |||
| 142 | if (errdet & ERRDET_CV) | ||
| 143 | dev_crit(ccf->dev, "Coherency Violation\n"); | ||
| 144 | |||
| 145 | if (cap_valid) { | ||
| 146 | dev_crit(ccf->dev, "address 0x%09llx, src id 0x%x\n", | ||
| 147 | addr, src_id); | ||
| 148 | } | ||
| 149 | |||
| 150 | out: | ||
| 151 | iowrite32be(errdet, &ccf->err_regs->errdet); | ||
| 152 | return errdet ? IRQ_HANDLED : IRQ_NONE; | ||
| 153 | } | ||
| 154 | |||
| 155 | static int ccf_probe(struct platform_device *pdev) | ||
| 156 | { | ||
| 157 | struct ccf_private *ccf; | ||
| 158 | struct resource *r; | ||
| 159 | const struct of_device_id *match; | ||
| 160 | int ret, irq; | ||
| 161 | |||
| 162 | match = of_match_device(ccf_matches, &pdev->dev); | ||
| 163 | if (WARN_ON(!match)) | ||
| 164 | return -ENODEV; | ||
| 165 | |||
| 166 | ccf = devm_kzalloc(&pdev->dev, sizeof(*ccf), GFP_KERNEL); | ||
| 167 | if (!ccf) | ||
| 168 | return -ENOMEM; | ||
| 169 | |||
| 170 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 171 | if (!r) { | ||
| 172 | dev_err(&pdev->dev, "%s: no mem resource\n", __func__); | ||
| 173 | return -ENXIO; | ||
| 174 | } | ||
| 175 | |||
| 176 | ccf->regs = devm_ioremap_resource(&pdev->dev, r); | ||
| 177 | if (IS_ERR(ccf->regs)) { | ||
| 178 | dev_err(&pdev->dev, "%s: can't map mem resource\n", __func__); | ||
| 179 | return PTR_ERR(ccf->regs); | ||
| 180 | } | ||
| 181 | |||
| 182 | ccf->dev = &pdev->dev; | ||
| 183 | ccf->info = match->data; | ||
| 184 | ccf->err_regs = ccf->regs + ccf->info->err_reg_offs; | ||
| 185 | |||
| 186 | dev_set_drvdata(&pdev->dev, ccf); | ||
| 187 | |||
| 188 | irq = platform_get_irq(pdev, 0); | ||
| 189 | if (!irq) { | ||
| 190 | dev_err(&pdev->dev, "%s: no irq\n", __func__); | ||
| 191 | return -ENXIO; | ||
| 192 | } | ||
| 193 | |||
| 194 | ret = devm_request_irq(&pdev->dev, irq, ccf_irq, 0, pdev->name, ccf); | ||
| 195 | if (ret) { | ||
| 196 | dev_err(&pdev->dev, "%s: can't request irq\n", __func__); | ||
| 197 | return ret; | ||
| 198 | } | ||
| 199 | |||
| 200 | switch (ccf->info->version) { | ||
| 201 | case CCF1: | ||
| 202 | /* On CCF1 this register enables rather than disables. */ | ||
| 203 | iowrite32be(ERRDET_LAE | ERRDET_CV, &ccf->err_regs->errdis); | ||
| 204 | break; | ||
| 205 | |||
| 206 | case CCF2: | ||
| 207 | iowrite32be(0, &ccf->err_regs->errdis); | ||
| 208 | iowrite32be(ERRDET_LAE | ERRDET_CV, &ccf->err_regs->errinten); | ||
| 209 | break; | ||
| 210 | } | ||
| 211 | |||
| 212 | return 0; | ||
| 213 | } | ||
| 214 | |||
| 215 | static int ccf_remove(struct platform_device *pdev) | ||
| 216 | { | ||
| 217 | struct ccf_private *ccf = dev_get_drvdata(&pdev->dev); | ||
| 218 | |||
| 219 | switch (ccf->info->version) { | ||
| 220 | case CCF1: | ||
| 221 | iowrite32be(0, &ccf->err_regs->errdis); | ||
| 222 | break; | ||
| 223 | |||
| 224 | case CCF2: | ||
| 225 | /* | ||
| 226 | * We clear errdis on ccf1 because that's the only way to | ||
| 227 | * disable interrupts, but on ccf2 there's no need to disable | ||
| 228 | * detection. | ||
| 229 | */ | ||
| 230 | iowrite32be(0, &ccf->err_regs->errinten); | ||
| 231 | break; | ||
| 232 | } | ||
| 233 | |||
| 234 | return 0; | ||
| 235 | } | ||
| 236 | |||
| 237 | static struct platform_driver ccf_driver = { | ||
| 238 | .driver = { | ||
| 239 | .name = KBUILD_MODNAME, | ||
| 240 | .owner = THIS_MODULE, | ||
| 241 | .of_match_table = ccf_matches, | ||
| 242 | }, | ||
| 243 | .probe = ccf_probe, | ||
| 244 | .remove = ccf_remove, | ||
| 245 | }; | ||
| 246 | |||
| 247 | module_platform_driver(ccf_driver); | ||
| 248 | |||
| 249 | MODULE_LICENSE("GPL"); | ||
| 250 | MODULE_AUTHOR("Freescale Semiconductor"); | ||
| 251 | MODULE_DESCRIPTION("Freescale CoreNet Coherency Fabric error reporting"); | ||
diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c index fc5413488496..1eedfba2ad3c 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c | |||
| @@ -41,7 +41,6 @@ | |||
| 41 | #ifdef CONFIG_8xx | 41 | #ifdef CONFIG_8xx |
| 42 | #include <asm/8xx_immap.h> | 42 | #include <asm/8xx_immap.h> |
| 43 | #include <asm/pgtable.h> | 43 | #include <asm/pgtable.h> |
| 44 | #include <asm/mpc8xx.h> | ||
| 45 | #include <asm/cpm1.h> | 44 | #include <asm/cpm1.h> |
| 46 | #endif | 45 | #endif |
| 47 | 46 | ||
diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-scc.c b/drivers/net/ethernet/freescale/fs_enet/mac-scc.c index b4bf02f57d43..90b3b19b7cd3 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-scc.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-scc.c | |||
| @@ -40,7 +40,6 @@ | |||
| 40 | #ifdef CONFIG_8xx | 40 | #ifdef CONFIG_8xx |
| 41 | #include <asm/8xx_immap.h> | 41 | #include <asm/8xx_immap.h> |
| 42 | #include <asm/pgtable.h> | 42 | #include <asm/pgtable.h> |
| 43 | #include <asm/mpc8xx.h> | ||
| 44 | #include <asm/cpm1.h> | 43 | #include <asm/cpm1.h> |
| 45 | #endif | 44 | #endif |
| 46 | 45 | ||
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 51cf8083b299..b0ce7cdee0c2 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig | |||
| @@ -144,16 +144,6 @@ config TCIC | |||
| 144 | "Bridge" is the name used for the hardware inside your computer that | 144 | "Bridge" is the name used for the hardware inside your computer that |
| 145 | PCMCIA cards are plugged into. If unsure, say N. | 145 | PCMCIA cards are plugged into. If unsure, say N. |
| 146 | 146 | ||
| 147 | config PCMCIA_M8XX | ||
| 148 | tristate "MPC8xx PCMCIA support" | ||
| 149 | depends on PCCARD && PPC && 8xx | ||
| 150 | select PCCARD_IODYN if PCMCIA != n | ||
| 151 | help | ||
| 152 | Say Y here to include support for PowerPC 8xx series PCMCIA | ||
| 153 | controller. | ||
| 154 | |||
| 155 | This driver is also available as a module called m8xx_pcmcia. | ||
| 156 | |||
| 157 | config PCMCIA_ALCHEMY_DEVBOARD | 147 | config PCMCIA_ALCHEMY_DEVBOARD |
| 158 | tristate "Alchemy Db/Pb1xxx PCMCIA socket services" | 148 | tristate "Alchemy Db/Pb1xxx PCMCIA socket services" |
| 159 | depends on MIPS_ALCHEMY && PCMCIA | 149 | depends on MIPS_ALCHEMY && PCMCIA |
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index fd55a6951402..27e94b30cf96 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile | |||
| @@ -23,7 +23,6 @@ obj-$(CONFIG_PD6729) += pd6729.o | |||
| 23 | obj-$(CONFIG_I82365) += i82365.o | 23 | obj-$(CONFIG_I82365) += i82365.o |
| 24 | obj-$(CONFIG_I82092) += i82092.o | 24 | obj-$(CONFIG_I82092) += i82092.o |
| 25 | obj-$(CONFIG_TCIC) += tcic.o | 25 | obj-$(CONFIG_TCIC) += tcic.o |
| 26 | obj-$(CONFIG_PCMCIA_M8XX) += m8xx_pcmcia.o | ||
| 27 | obj-$(CONFIG_PCMCIA_SOC_COMMON) += soc_common.o | 26 | obj-$(CONFIG_PCMCIA_SOC_COMMON) += soc_common.o |
| 28 | obj-$(CONFIG_PCMCIA_SA11XX_BASE) += sa11xx_base.o | 27 | obj-$(CONFIG_PCMCIA_SA11XX_BASE) += sa11xx_base.o |
| 29 | obj-$(CONFIG_PCMCIA_SA1100) += sa1100_cs.o | 28 | obj-$(CONFIG_PCMCIA_SA1100) += sa1100_cs.o |
diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c deleted file mode 100644 index 182034d2ef58..000000000000 --- a/drivers/pcmcia/m8xx_pcmcia.c +++ /dev/null | |||
| @@ -1,1168 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * m8xx_pcmcia.c - Linux PCMCIA socket driver for the mpc8xx series. | ||
| 3 | * | ||
| 4 | * (C) 1999-2000 Magnus Damm <damm@opensource.se> | ||
| 5 | * (C) 2001-2002 Montavista Software, Inc. | ||
| 6 | * <mlocke@mvista.com> | ||
| 7 | * | ||
| 8 | * Support for two slots by Cyclades Corporation | ||
| 9 | * <oliver.kurth@cyclades.de> | ||
| 10 | * Further fixes, v2.6 kernel port | ||
| 11 | * <marcelo.tosatti@cyclades.com> | ||
| 12 | * | ||
| 13 | * Some fixes, additions (C) 2005-2007 Montavista Software, Inc. | ||
| 14 | * <vbordug@ru.mvista.com> | ||
| 15 | * | ||
| 16 | * "The ExCA standard specifies that socket controllers should provide | ||
| 17 | * two IO and five memory windows per socket, which can be independently | ||
| 18 | * configured and positioned in the host address space and mapped to | ||
| 19 | * arbitrary segments of card address space. " - David A Hinds. 1999 | ||
| 20 | * | ||
| 21 | * This controller does _not_ meet the ExCA standard. | ||
| 22 | * | ||
| 23 | * m8xx pcmcia controller brief info: | ||
| 24 | * + 8 windows (attrib, mem, i/o) | ||
| 25 | * + up to two slots (SLOT_A and SLOT_B) | ||
| 26 | * + inputpins, outputpins, event and mask registers. | ||
| 27 | * - no offset register. sigh. | ||
| 28 | * | ||
| 29 | * Because of the lacking offset register we must map the whole card. | ||
| 30 | * We assign each memory window PCMCIA_MEM_WIN_SIZE address space. | ||
| 31 | * Make sure there is (PCMCIA_MEM_WIN_SIZE * PCMCIA_MEM_WIN_NO | ||
| 32 | * * PCMCIA_SOCKETS_NO) bytes at PCMCIA_MEM_WIN_BASE. | ||
| 33 | * The i/o windows are dynamically allocated at PCMCIA_IO_WIN_BASE. | ||
| 34 | * They are maximum 64KByte each... | ||
| 35 | */ | ||
| 36 | |||
| 37 | #include <linux/module.h> | ||
| 38 | #include <linux/init.h> | ||
| 39 | #include <linux/types.h> | ||
| 40 | #include <linux/fcntl.h> | ||
| 41 | #include <linux/string.h> | ||
| 42 | |||
| 43 | #include <linux/kernel.h> | ||
| 44 | #include <linux/errno.h> | ||
| 45 | #include <linux/timer.h> | ||
| 46 | #include <linux/ioport.h> | ||
| 47 | #include <linux/delay.h> | ||
| 48 | #include <linux/interrupt.h> | ||
| 49 | #include <linux/fsl_devices.h> | ||
| 50 | #include <linux/bitops.h> | ||
| 51 | #include <linux/of_address.h> | ||
| 52 | #include <linux/of_device.h> | ||
| 53 | #include <linux/of_irq.h> | ||
| 54 | #include <linux/of_platform.h> | ||
| 55 | |||
| 56 | #include <asm/io.h> | ||
| 57 | #include <asm/time.h> | ||
| 58 | #include <asm/mpc8xx.h> | ||
| 59 | #include <asm/8xx_immap.h> | ||
| 60 | #include <asm/irq.h> | ||
| 61 | #include <asm/fs_pd.h> | ||
| 62 | |||
| 63 | #include <pcmcia/ss.h> | ||
| 64 | |||
| 65 | #define pcmcia_info(args...) printk(KERN_INFO "m8xx_pcmcia: "args) | ||
| 66 | #define pcmcia_error(args...) printk(KERN_ERR "m8xx_pcmcia: "args) | ||
| 67 | |||
| 68 | static const char *version = "Version 0.06, Aug 2005"; | ||
| 69 | MODULE_LICENSE("Dual MPL/GPL"); | ||
| 70 | |||
| 71 | #if !defined(CONFIG_PCMCIA_SLOT_A) && !defined(CONFIG_PCMCIA_SLOT_B) | ||
| 72 | |||
| 73 | /* The ADS board use SLOT_A */ | ||
| 74 | #ifdef CONFIG_ADS | ||
| 75 | #define CONFIG_PCMCIA_SLOT_A | ||
| 76 | #define CONFIG_BD_IS_MHZ | ||
| 77 | #endif | ||
| 78 | |||
| 79 | /* The FADS series are a mess */ | ||
| 80 | #ifdef CONFIG_FADS | ||
| 81 | #if defined(CONFIG_MPC860T) || defined(CONFIG_MPC860) || defined(CONFIG_MPC821) | ||
| 82 | #define CONFIG_PCMCIA_SLOT_A | ||
| 83 | #else | ||
| 84 | #define CONFIG_PCMCIA_SLOT_B | ||
| 85 | #endif | ||
| 86 | #endif | ||
| 87 | |||
| 88 | #if defined(CONFIG_MPC885ADS) | ||
| 89 | #define CONFIG_PCMCIA_SLOT_A | ||
| 90 | #define PCMCIA_GLITCHY_CD | ||
| 91 | #endif | ||
| 92 | |||
| 93 | /* Cyclades ACS uses both slots */ | ||
| 94 | #ifdef CONFIG_PRxK | ||
| 95 | #define CONFIG_PCMCIA_SLOT_A | ||
| 96 | #define CONFIG_PCMCIA_SLOT_B | ||
| 97 | #endif | ||
| 98 | |||
| 99 | #endif /* !defined(CONFIG_PCMCIA_SLOT_A) && !defined(CONFIG_PCMCIA_SLOT_B) */ | ||
| 100 | |||
| 101 | #if defined(CONFIG_PCMCIA_SLOT_A) && defined(CONFIG_PCMCIA_SLOT_B) | ||
| 102 | |||
| 103 | #define PCMCIA_SOCKETS_NO 2 | ||
| 104 | /* We have only 8 windows, dualsocket support will be limited. */ | ||
| 105 | #define PCMCIA_MEM_WIN_NO 2 | ||
| 106 | #define PCMCIA_IO_WIN_NO 2 | ||
| 107 | #define PCMCIA_SLOT_MSG "SLOT_A and SLOT_B" | ||
| 108 | |||
| 109 | #elif defined(CONFIG_PCMCIA_SLOT_A) || defined(CONFIG_PCMCIA_SLOT_B) | ||
| 110 | |||
| 111 | #define PCMCIA_SOCKETS_NO 1 | ||
| 112 | /* full support for one slot */ | ||
| 113 | #define PCMCIA_MEM_WIN_NO 5 | ||
| 114 | #define PCMCIA_IO_WIN_NO 2 | ||
| 115 | |||
| 116 | /* define _slot_ to be able to optimize macros */ | ||
| 117 | |||
| 118 | #ifdef CONFIG_PCMCIA_SLOT_A | ||
| 119 | #define _slot_ 0 | ||
| 120 | #define PCMCIA_SLOT_MSG "SLOT_A" | ||
| 121 | #else | ||
| 122 | #define _slot_ 1 | ||
| 123 | #define PCMCIA_SLOT_MSG "SLOT_B" | ||
| 124 | #endif | ||
| 125 | |||
| 126 | #else | ||
| 127 | #error m8xx_pcmcia: Bad configuration! | ||
| 128 | #endif | ||
| 129 | |||
| 130 | /* ------------------------------------------------------------------------- */ | ||
| 131 | |||
| 132 | #define PCMCIA_MEM_WIN_BASE 0xe0000000 /* base address for memory window 0 */ | ||
| 133 | #define PCMCIA_MEM_WIN_SIZE 0x04000000 /* each memory window is 64 MByte */ | ||
| 134 | #define PCMCIA_IO_WIN_BASE _IO_BASE /* base address for io window 0 */ | ||
| 135 | /* ------------------------------------------------------------------------- */ | ||
| 136 | |||
| 137 | static int pcmcia_schlvl; | ||
| 138 | |||
| 139 | static DEFINE_SPINLOCK(events_lock); | ||
| 140 | |||
| 141 | #define PCMCIA_SOCKET_KEY_5V 1 | ||
| 142 | #define PCMCIA_SOCKET_KEY_LV 2 | ||
| 143 | |||
| 144 | /* look up table for pgcrx registers */ | ||
| 145 | static u32 *m8xx_pgcrx[2]; | ||
| 146 | |||
| 147 | /* | ||
| 148 | * This structure is used to address each window in the PCMCIA controller. | ||
| 149 | * | ||
| 150 | * Keep in mind that we assume that pcmcia_win[n+1] is mapped directly | ||
| 151 | * after pcmcia_win[n]... | ||
| 152 | */ | ||
| 153 | |||
| 154 | struct pcmcia_win { | ||
| 155 | u32 br; | ||
| 156 | u32 or; | ||
| 157 | }; | ||
| 158 | |||
| 159 | /* | ||
| 160 | * For some reason the hardware guys decided to make both slots share | ||
| 161 | * some registers. | ||
| 162 | * | ||
| 163 | * Could someone invent object oriented hardware ? | ||
| 164 | * | ||
| 165 | * The macros are used to get the right bit from the registers. | ||
| 166 | * SLOT_A : slot = 0 | ||
| 167 | * SLOT_B : slot = 1 | ||
| 168 | */ | ||
| 169 | |||
| 170 | #define M8XX_PCMCIA_VS1(slot) (0x80000000 >> (slot << 4)) | ||
| 171 | #define M8XX_PCMCIA_VS2(slot) (0x40000000 >> (slot << 4)) | ||
| 172 | #define M8XX_PCMCIA_VS_MASK(slot) (0xc0000000 >> (slot << 4)) | ||
| 173 | #define M8XX_PCMCIA_VS_SHIFT(slot) (30 - (slot << 4)) | ||
| 174 | |||
| 175 | #define M8XX_PCMCIA_WP(slot) (0x20000000 >> (slot << 4)) | ||
| 176 | #define M8XX_PCMCIA_CD2(slot) (0x10000000 >> (slot << 4)) | ||
| 177 | #define M8XX_PCMCIA_CD1(slot) (0x08000000 >> (slot << 4)) | ||
| 178 | #define M8XX_PCMCIA_BVD2(slot) (0x04000000 >> (slot << 4)) | ||
| 179 | #define M8XX_PCMCIA_BVD1(slot) (0x02000000 >> (slot << 4)) | ||
| 180 | #define M8XX_PCMCIA_RDY(slot) (0x01000000 >> (slot << 4)) | ||
| 181 | #define M8XX_PCMCIA_RDY_L(slot) (0x00800000 >> (slot << 4)) | ||
| 182 | #define M8XX_PCMCIA_RDY_H(slot) (0x00400000 >> (slot << 4)) | ||
| 183 | #define M8XX_PCMCIA_RDY_R(slot) (0x00200000 >> (slot << 4)) | ||
| 184 | #define M8XX_PCMCIA_RDY_F(slot) (0x00100000 >> (slot << 4)) | ||
| 185 | #define M8XX_PCMCIA_MASK(slot) (0xFFFF0000 >> (slot << 4)) | ||
| 186 | |||
| 187 | #define M8XX_PCMCIA_POR_VALID 0x00000001 | ||
| 188 | #define M8XX_PCMCIA_POR_WRPROT 0x00000002 | ||
| 189 | #define M8XX_PCMCIA_POR_ATTRMEM 0x00000010 | ||
| 190 | #define M8XX_PCMCIA_POR_IO 0x00000018 | ||
| 191 | #define M8XX_PCMCIA_POR_16BIT 0x00000040 | ||
| 192 | |||
| 193 | #define M8XX_PGCRX(slot) m8xx_pgcrx[slot] | ||
| 194 | |||
| 195 | #define M8XX_PGCRX_CXOE 0x00000080 | ||
| 196 | #define M8XX_PGCRX_CXRESET 0x00000040 | ||
| 197 | |||
| 198 | /* we keep one lookup table per socket to check flags */ | ||
| 199 | |||
| 200 | #define PCMCIA_EVENTS_MAX 5 /* 4 max at a time + termination */ | ||
| 201 | |||
| 202 | struct event_table { | ||
| 203 | u32 regbit; | ||
| 204 | u32 eventbit; | ||
| 205 | }; | ||
| 206 | |||
| 207 | static const char driver_name[] = "m8xx-pcmcia"; | ||
| 208 | |||
| 209 | struct socket_info { | ||
| 210 | void (*handler) (void *info, u32 events); | ||
| 211 | void *info; | ||
| 212 | |||
| 213 | u32 slot; | ||
| 214 | pcmconf8xx_t *pcmcia; | ||
| 215 | u32 bus_freq; | ||
| 216 | int hwirq; | ||
| 217 | |||
| 218 | socket_state_t state; | ||
| 219 | struct pccard_mem_map mem_win[PCMCIA_MEM_WIN_NO]; | ||
| 220 | struct pccard_io_map io_win[PCMCIA_IO_WIN_NO]; | ||
| 221 | struct event_table events[PCMCIA_EVENTS_MAX]; | ||
| 222 | struct pcmcia_socket socket; | ||
| 223 | }; | ||
| 224 | |||
| 225 | static struct socket_info socket[PCMCIA_SOCKETS_NO]; | ||
| 226 | |||
| 227 | /* | ||
| 228 | * Search this table to see if the windowsize is | ||
| 229 | * supported... | ||
| 230 | */ | ||
| 231 | |||
| 232 | #define M8XX_SIZES_NO 32 | ||
| 233 | |||
| 234 | static const u32 m8xx_size_to_gray[M8XX_SIZES_NO] = { | ||
| 235 | 0x00000001, 0x00000002, 0x00000008, 0x00000004, | ||
| 236 | 0x00000080, 0x00000040, 0x00000010, 0x00000020, | ||
| 237 | 0x00008000, 0x00004000, 0x00001000, 0x00002000, | ||
| 238 | 0x00000100, 0x00000200, 0x00000800, 0x00000400, | ||
| 239 | |||
| 240 | 0x0fffffff, 0xffffffff, 0xffffffff, 0xffffffff, | ||
| 241 | 0x01000000, 0x02000000, 0xffffffff, 0x04000000, | ||
| 242 | 0x00010000, 0x00020000, 0x00080000, 0x00040000, | ||
| 243 | 0x00800000, 0x00400000, 0x00100000, 0x00200000 | ||
| 244 | }; | ||
| 245 | |||
| 246 | /* ------------------------------------------------------------------------- */ | ||
| 247 | |||
| 248 | static irqreturn_t m8xx_interrupt(int irq, void *dev); | ||
| 249 | |||
| 250 | #define PCMCIA_BMT_LIMIT (15*4) /* Bus Monitor Timeout value */ | ||
| 251 | |||
| 252 | /* FADS Boards from Motorola */ | ||
| 253 | |||
| 254 | #if defined(CONFIG_FADS) | ||
| 255 | |||
| 256 | #define PCMCIA_BOARD_MSG "FADS" | ||
| 257 | |||
| 258 | static int voltage_set(int slot, int vcc, int vpp) | ||
| 259 | { | ||
| 260 | u32 reg = 0; | ||
| 261 | |||
| 262 | switch (vcc) { | ||
| 263 | case 0: | ||
| 264 | break; | ||
| 265 | case 33: | ||
| 266 | reg |= BCSR1_PCCVCC0; | ||
| 267 | break; | ||
| 268 | case 50: | ||
| 269 | reg |= BCSR1_PCCVCC1; | ||
| 270 | break; | ||
| 271 | default: | ||
| 272 | return 1; | ||
| 273 | } | ||
| 274 | |||
| 275 | switch (vpp) { | ||
| 276 | case 0: | ||
| 277 | break; | ||
| 278 | case 33: | ||
| 279 | case 50: | ||
| 280 | if (vcc == vpp) | ||
| 281 | reg |= BCSR1_PCCVPP1; | ||
| 282 | else | ||
| 283 | return 1; | ||
| 284 | break; | ||
| 285 | case 120: | ||
| 286 | if ((vcc == 33) || (vcc == 50)) | ||
| 287 | reg |= BCSR1_PCCVPP0; | ||
| 288 | else | ||
| 289 | return 1; | ||
| 290 | default: | ||
| 291 | return 1; | ||
| 292 | } | ||
| 293 | |||
| 294 | /* first, turn off all power */ | ||
| 295 | out_be32((u32 *) BCSR1, | ||
| 296 | in_be32((u32 *) BCSR1) & ~(BCSR1_PCCVCC_MASK | | ||
| 297 | BCSR1_PCCVPP_MASK)); | ||
| 298 | |||
| 299 | /* enable new powersettings */ | ||
| 300 | out_be32((u32 *) BCSR1, in_be32((u32 *) BCSR1) | reg); | ||
| 301 | |||
| 302 | return 0; | ||
| 303 | } | ||
| 304 | |||
| 305 | #define socket_get(_slot_) PCMCIA_SOCKET_KEY_5V | ||
| 306 | |||
| 307 | static void hardware_enable(int slot) | ||
| 308 | { | ||
| 309 | out_be32((u32 *) BCSR1, in_be32((u32 *) BCSR1) & ~BCSR1_PCCEN); | ||
| 310 | } | ||
| 311 | |||
| 312 | static void hardware_disable(int slot) | ||
| 313 | { | ||
| 314 | out_be32((u32 *) BCSR1, in_be32((u32 *) BCSR1) | BCSR1_PCCEN); | ||
| 315 | } | ||
| 316 | |||
| 317 | #endif | ||
| 318 | |||
| 319 | /* MPC885ADS Boards */ | ||
| 320 | |||
| 321 | #if defined(CONFIG_MPC885ADS) | ||
| 322 | |||
| 323 | #define PCMCIA_BOARD_MSG "MPC885ADS" | ||
| 324 | #define socket_get(_slot_) PCMCIA_SOCKET_KEY_5V | ||
| 325 | |||
| 326 | static inline void hardware_enable(int slot) | ||
| 327 | { | ||
| 328 | m8xx_pcmcia_ops.hw_ctrl(slot, 1); | ||
| 329 | } | ||
| 330 | |||
| 331 | static inline void hardware_disable(int slot) | ||
| 332 | { | ||
| 333 | m8xx_pcmcia_ops.hw_ctrl(slot, 0); | ||
| 334 | } | ||
| 335 | |||
| 336 | static inline int voltage_set(int slot, int vcc, int vpp) | ||
| 337 | { | ||
| 338 | return m8xx_pcmcia_ops.voltage_set(slot, vcc, vpp); | ||
| 339 | } | ||
| 340 | |||
| 341 | #endif | ||
| 342 | |||
| 343 | #if defined(CONFIG_PRxK) | ||
| 344 | #include <asm/cpld.h> | ||
| 345 | extern volatile fpga_pc_regs *fpga_pc; | ||
| 346 | |||
| 347 | #define PCMCIA_BOARD_MSG "MPC855T" | ||
| 348 | |||
| 349 | static int voltage_set(int slot, int vcc, int vpp) | ||
| 350 | { | ||
| 351 | u8 reg = 0; | ||
| 352 | u8 regread; | ||
| 353 | cpld_regs *ccpld = get_cpld(); | ||
| 354 | |||
| 355 | switch (vcc) { | ||
| 356 | case 0: | ||
| 357 | break; | ||
| 358 | case 33: | ||
| 359 | reg |= PCMCIA_VCC_33; | ||
| 360 | break; | ||
| 361 | case 50: | ||
| 362 | reg |= PCMCIA_VCC_50; | ||
| 363 | break; | ||
| 364 | default: | ||
| 365 | return 1; | ||
| 366 | } | ||
| 367 | |||
| 368 | switch (vpp) { | ||
| 369 | case 0: | ||
| 370 | break; | ||
| 371 | case 33: | ||
| 372 | case 50: | ||
| 373 | if (vcc == vpp) | ||
| 374 | reg |= PCMCIA_VPP_VCC; | ||
| 375 | else | ||
| 376 | return 1; | ||
| 377 | break; | ||
| 378 | case 120: | ||
| 379 | if ((vcc == 33) || (vcc == 50)) | ||
| 380 | reg |= PCMCIA_VPP_12; | ||
| 381 | else | ||
| 382 | return 1; | ||
| 383 | default: | ||
| 384 | return 1; | ||
| 385 | } | ||
| 386 | |||
| 387 | reg = reg >> (slot << 2); | ||
| 388 | regread = in_8(&ccpld->fpga_pc_ctl); | ||
| 389 | if (reg != | ||
| 390 | (regread & ((PCMCIA_VCC_MASK | PCMCIA_VPP_MASK) >> (slot << 2)))) { | ||
| 391 | /* enable new powersettings */ | ||
| 392 | regread = | ||
| 393 | regread & ~((PCMCIA_VCC_MASK | PCMCIA_VPP_MASK) >> | ||
| 394 | (slot << 2)); | ||
| 395 | out_8(&ccpld->fpga_pc_ctl, reg | regread); | ||
| 396 | msleep(100); | ||
| 397 | } | ||
| 398 | |||
| 399 | return 0; | ||
| 400 | } | ||
| 401 | |||
| 402 | #define socket_get(_slot_) PCMCIA_SOCKET_KEY_LV | ||
| 403 | #define hardware_enable(_slot_) /* No hardware to enable */ | ||
| 404 | #define hardware_disable(_slot_) /* No hardware to disable */ | ||
| 405 | |||
| 406 | #endif /* CONFIG_PRxK */ | ||
| 407 | |||
| 408 | static u32 pending_events[PCMCIA_SOCKETS_NO]; | ||
| 409 | static DEFINE_SPINLOCK(pending_event_lock); | ||
| 410 | |||
| 411 | static irqreturn_t m8xx_interrupt(int irq, void *dev) | ||
| 412 | { | ||
| 413 | struct socket_info *s; | ||
| 414 | struct event_table *e; | ||
| 415 | unsigned int i, events, pscr, pipr, per; | ||
| 416 | pcmconf8xx_t *pcmcia = socket[0].pcmcia; | ||
| 417 | |||
| 418 | pr_debug("m8xx_pcmcia: Interrupt!\n"); | ||
| 419 | /* get interrupt sources */ | ||
| 420 | |||
| 421 | pscr = in_be32(&pcmcia->pcmc_pscr); | ||
| 422 | pipr = in_be32(&pcmcia->pcmc_pipr); | ||
| 423 | per = in_be32(&pcmcia->pcmc_per); | ||
| 424 | |||
| 425 | for (i = 0; i < PCMCIA_SOCKETS_NO; i++) { | ||
| 426 | s = &socket[i]; | ||
| 427 | e = &s->events[0]; | ||
| 428 | events = 0; | ||
| 429 | |||
| 430 | while (e->regbit) { | ||
| 431 | if (pscr & e->regbit) | ||
| 432 | events |= e->eventbit; | ||
| 433 | |||
| 434 | e++; | ||
| 435 | } | ||
| 436 | |||
| 437 | /* | ||
| 438 | * report only if both card detect signals are the same | ||
| 439 | * not too nice done, | ||
| 440 | * we depend on that CD2 is the bit to the left of CD1... | ||
| 441 | */ | ||
| 442 | if (events & SS_DETECT) | ||
| 443 | if (((pipr & M8XX_PCMCIA_CD2(i)) >> 1) ^ | ||
| 444 | (pipr & M8XX_PCMCIA_CD1(i))) { | ||
| 445 | events &= ~SS_DETECT; | ||
| 446 | } | ||
| 447 | #ifdef PCMCIA_GLITCHY_CD | ||
| 448 | /* | ||
| 449 | * I've experienced CD problems with my ADS board. | ||
| 450 | * We make an extra check to see if there was a | ||
| 451 | * real change of Card detection. | ||
| 452 | */ | ||
| 453 | |||
| 454 | if ((events & SS_DETECT) && | ||
| 455 | ((pipr & | ||
| 456 | (M8XX_PCMCIA_CD2(i) | M8XX_PCMCIA_CD1(i))) == 0) && | ||
| 457 | (s->state.Vcc | s->state.Vpp)) { | ||
| 458 | events &= ~SS_DETECT; | ||
| 459 | /*printk( "CD glitch workaround - CD = 0x%08x!\n", | ||
| 460 | (pipr & (M8XX_PCMCIA_CD2(i) | ||
| 461 | | M8XX_PCMCIA_CD1(i)))); */ | ||
| 462 | } | ||
| 463 | #endif | ||
| 464 | |||
| 465 | /* call the handler */ | ||
| 466 | |||
| 467 | pr_debug("m8xx_pcmcia: slot %u: events = 0x%02x, pscr = 0x%08x, " | ||
| 468 | "pipr = 0x%08x\n", i, events, pscr, pipr); | ||
| 469 | |||
| 470 | if (events) { | ||
| 471 | spin_lock(&pending_event_lock); | ||
| 472 | pending_events[i] |= events; | ||
| 473 | spin_unlock(&pending_event_lock); | ||
| 474 | /* | ||
| 475 | * Turn off RDY_L bits in the PER mask on | ||
| 476 | * CD interrupt receival. | ||
| 477 | * | ||
| 478 | * They can generate bad interrupts on the | ||
| 479 | * ACS4,8,16,32. - marcelo | ||
| 480 | */ | ||
| 481 | per &= ~M8XX_PCMCIA_RDY_L(0); | ||
| 482 | per &= ~M8XX_PCMCIA_RDY_L(1); | ||
| 483 | |||
| 484 | out_be32(&pcmcia->pcmc_per, per); | ||
| 485 | |||
| 486 | if (events) | ||
| 487 | pcmcia_parse_events(&socket[i].socket, events); | ||
| 488 | } | ||
| 489 | } | ||
| 490 | |||
| 491 | /* clear the interrupt sources */ | ||
| 492 | out_be32(&pcmcia->pcmc_pscr, pscr); | ||
| 493 | |||
| 494 | pr_debug("m8xx_pcmcia: Interrupt done.\n"); | ||
| 495 | |||
| 496 | return IRQ_HANDLED; | ||
| 497 | } | ||
| 498 | |||
| 499 | static u32 m8xx_get_graycode(u32 size) | ||
| 500 | { | ||
| 501 | u32 k; | ||
| 502 | |||
| 503 | for (k = 0; k < M8XX_SIZES_NO; k++) | ||
| 504 | if (m8xx_size_to_gray[k] == size) | ||
| 505 | break; | ||
| 506 | |||
| 507 | if ((k == M8XX_SIZES_NO) || (m8xx_size_to_gray[k] == -1)) | ||
| 508 | k = -1; | ||
| 509 | |||
| 510 | return k; | ||
| 511 | } | ||
| 512 | |||
| 513 | static u32 m8xx_get_speed(u32 ns, u32 is_io, u32 bus_freq) | ||
| 514 | { | ||
| 515 | u32 reg, clocks, psst, psl, psht; | ||
| 516 | |||
| 517 | if (!ns) { | ||
| 518 | |||
| 519 | /* | ||
| 520 | * We get called with IO maps setup to 0ns | ||
| 521 | * if not specified by the user. | ||
| 522 | * They should be 255ns. | ||
| 523 | */ | ||
| 524 | |||
| 525 | if (is_io) | ||
| 526 | ns = 255; | ||
| 527 | else | ||
| 528 | ns = 100; /* fast memory if 0 */ | ||
| 529 | } | ||
| 530 | |||
| 531 | /* | ||
| 532 | * In PSST, PSL, PSHT fields we tell the controller | ||
| 533 | * timing parameters in CLKOUT clock cycles. | ||
| 534 | * CLKOUT is the same as GCLK2_50. | ||
| 535 | */ | ||
| 536 | |||
| 537 | /* how we want to adjust the timing - in percent */ | ||
| 538 | |||
| 539 | #define ADJ 180 /* 80 % longer accesstime - to be sure */ | ||
| 540 | |||
| 541 | clocks = ((bus_freq / 1000) * ns) / 1000; | ||
| 542 | clocks = (clocks * ADJ) / (100 * 1000); | ||
| 543 | if (clocks >= PCMCIA_BMT_LIMIT) { | ||
| 544 | printk("Max access time limit reached\n"); | ||
| 545 | clocks = PCMCIA_BMT_LIMIT - 1; | ||
| 546 | } | ||
| 547 | |||
| 548 | psst = clocks / 7; /* setup time */ | ||
| 549 | psht = clocks / 7; /* hold time */ | ||
| 550 | psl = (clocks * 5) / 7; /* strobe length */ | ||
| 551 | |||
| 552 | psst += clocks - (psst + psht + psl); | ||
| 553 | |||
| 554 | reg = psst << 12; | ||
| 555 | reg |= psl << 7; | ||
| 556 | reg |= psht << 16; | ||
| 557 | |||
| 558 | return reg; | ||
| 559 | } | ||
| 560 | |||
| 561 | static int m8xx_get_status(struct pcmcia_socket *sock, unsigned int *value) | ||
| 562 | { | ||
| 563 | int lsock = container_of(sock, struct socket_info, socket)->slot; | ||
| 564 | struct socket_info *s = &socket[lsock]; | ||
| 565 | unsigned int pipr, reg; | ||
| 566 | pcmconf8xx_t *pcmcia = s->pcmcia; | ||
| 567 | |||
| 568 | pipr = in_be32(&pcmcia->pcmc_pipr); | ||
| 569 | |||
| 570 | *value = ((pipr & (M8XX_PCMCIA_CD1(lsock) | ||
| 571 | | M8XX_PCMCIA_CD2(lsock))) == 0) ? SS_DETECT : 0; | ||
| 572 | *value |= (pipr & M8XX_PCMCIA_WP(lsock)) ? SS_WRPROT : 0; | ||
| 573 | |||
| 574 | if (s->state.flags & SS_IOCARD) | ||
| 575 | *value |= (pipr & M8XX_PCMCIA_BVD1(lsock)) ? SS_STSCHG : 0; | ||
| 576 | else { | ||
| 577 | *value |= (pipr & M8XX_PCMCIA_RDY(lsock)) ? SS_READY : 0; | ||
| 578 | *value |= (pipr & M8XX_PCMCIA_BVD1(lsock)) ? SS_BATDEAD : 0; | ||
| 579 | *value |= (pipr & M8XX_PCMCIA_BVD2(lsock)) ? SS_BATWARN : 0; | ||
| 580 | } | ||
| 581 | |||
| 582 | if (s->state.Vcc | s->state.Vpp) | ||
| 583 | *value |= SS_POWERON; | ||
| 584 | |||
| 585 | /* | ||
| 586 | * Voltage detection: | ||
| 587 | * This driver only supports 16-Bit pc-cards. | ||
| 588 | * Cardbus is not handled here. | ||
| 589 | * | ||
| 590 | * To determine what voltage to use we must read the VS1 and VS2 pin. | ||
| 591 | * Depending on what socket type is present, | ||
| 592 | * different combinations mean different things. | ||
| 593 | * | ||
| 594 | * Card Key Socket Key VS1 VS2 Card Vcc for CIS parse | ||
| 595 | * | ||
| 596 | * 5V 5V, LV* NC NC 5V only 5V (if available) | ||
| 597 | * | ||
| 598 | * 5V 5V, LV* GND NC 5 or 3.3V as low as possible | ||
| 599 | * | ||
| 600 | * 5V 5V, LV* GND GND 5, 3.3, x.xV as low as possible | ||
| 601 | * | ||
| 602 | * LV* 5V - - shall not fit into socket | ||
| 603 | * | ||
| 604 | * LV* LV* GND NC 3.3V only 3.3V | ||
| 605 | * | ||
| 606 | * LV* LV* NC GND x.xV x.xV (if avail.) | ||
| 607 | * | ||
| 608 | * LV* LV* GND GND 3.3 or x.xV as low as possible | ||
| 609 | * | ||
| 610 | * *LV means Low Voltage | ||
| 611 | * | ||
| 612 | * | ||
| 613 | * That gives us the following table: | ||
| 614 | * | ||
| 615 | * Socket VS1 VS2 Voltage | ||
| 616 | * | ||
| 617 | * 5V NC NC 5V | ||
| 618 | * 5V NC GND none (should not be possible) | ||
| 619 | * 5V GND NC >= 3.3V | ||
| 620 | * 5V GND GND >= x.xV | ||
| 621 | * | ||
| 622 | * LV NC NC 5V (if available) | ||
| 623 | * LV NC GND x.xV (if available) | ||
| 624 | * LV GND NC 3.3V | ||
| 625 | * LV GND GND >= x.xV | ||
| 626 | * | ||
| 627 | * So, how do I determine if I have a 5V or a LV | ||
| 628 | * socket on my board? Look at the socket! | ||
| 629 | * | ||
| 630 | * | ||
| 631 | * Socket with 5V key: | ||
| 632 | * ++--------------------------------------------+ | ||
| 633 | * || | | ||
| 634 | * || || | ||
| 635 | * || || | ||
| 636 | * | | | ||
| 637 | * +---------------------------------------------+ | ||
| 638 | * | ||
| 639 | * Socket with LV key: | ||
| 640 | * ++--------------------------------------------+ | ||
| 641 | * || | | ||
| 642 | * | || | ||
| 643 | * | || | ||
| 644 | * | | | ||
| 645 | * +---------------------------------------------+ | ||
| 646 | * | ||
| 647 | * | ||
| 648 | * With other words - LV only cards does not fit | ||
| 649 | * into the 5V socket! | ||
| 650 | */ | ||
| 651 | |||
| 652 | /* read out VS1 and VS2 */ | ||
| 653 | |||
| 654 | reg = (pipr & M8XX_PCMCIA_VS_MASK(lsock)) | ||
| 655 | >> M8XX_PCMCIA_VS_SHIFT(lsock); | ||
| 656 | |||
| 657 | if (socket_get(lsock) == PCMCIA_SOCKET_KEY_LV) { | ||
| 658 | switch (reg) { | ||
| 659 | case 1: | ||
| 660 | *value |= SS_3VCARD; | ||
| 661 | break; /* GND, NC - 3.3V only */ | ||
| 662 | case 2: | ||
| 663 | *value |= SS_XVCARD; | ||
| 664 | break; /* NC. GND - x.xV only */ | ||
| 665 | }; | ||
| 666 | } | ||
| 667 | |||
| 668 | pr_debug("m8xx_pcmcia: GetStatus(%d) = %#2.2x\n", lsock, *value); | ||
| 669 | return 0; | ||
| 670 | } | ||
| 671 | |||
| 672 | static int m8xx_set_socket(struct pcmcia_socket *sock, socket_state_t * state) | ||
| 673 | { | ||
| 674 | int lsock = container_of(sock, struct socket_info, socket)->slot; | ||
| 675 | struct socket_info *s = &socket[lsock]; | ||
| 676 | struct event_table *e; | ||
| 677 | unsigned int reg; | ||
| 678 | unsigned long flags; | ||
| 679 | pcmconf8xx_t *pcmcia = socket[0].pcmcia; | ||
| 680 | |||
| 681 | pr_debug("m8xx_pcmcia: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " | ||
| 682 | "io_irq %d, csc_mask %#2.2x)\n", lsock, state->flags, | ||
| 683 | state->Vcc, state->Vpp, state->io_irq, state->csc_mask); | ||
| 684 | |||
| 685 | /* First, set voltage - bail out if invalid */ | ||
| 686 | if (voltage_set(lsock, state->Vcc, state->Vpp)) | ||
| 687 | return -EINVAL; | ||
| 688 | |||
| 689 | /* Take care of reset... */ | ||
| 690 | if (state->flags & SS_RESET) | ||
| 691 | out_be32(M8XX_PGCRX(lsock), in_be32(M8XX_PGCRX(lsock)) | M8XX_PGCRX_CXRESET); /* active high */ | ||
| 692 | else | ||
| 693 | out_be32(M8XX_PGCRX(lsock), | ||
| 694 | in_be32(M8XX_PGCRX(lsock)) & ~M8XX_PGCRX_CXRESET); | ||
| 695 | |||
| 696 | /* ... and output enable. */ | ||
| 697 | |||
| 698 | /* The CxOE signal is connected to a 74541 on the ADS. | ||
| 699 | I guess most other boards used the ADS as a reference. | ||
| 700 | I tried to control the CxOE signal with SS_OUTPUT_ENA, | ||
| 701 | but the reset signal seems connected via the 541. | ||
| 702 | If the CxOE is left high are some signals tristated and | ||
| 703 | no pullups are present -> the cards act weird. | ||
| 704 | So right now the buffers are enabled if the power is on. */ | ||
| 705 | |||
| 706 | if (state->Vcc || state->Vpp) | ||
| 707 | out_be32(M8XX_PGCRX(lsock), in_be32(M8XX_PGCRX(lsock)) & ~M8XX_PGCRX_CXOE); /* active low */ | ||
| 708 | else | ||
| 709 | out_be32(M8XX_PGCRX(lsock), | ||
| 710 | in_be32(M8XX_PGCRX(lsock)) | M8XX_PGCRX_CXOE); | ||
| 711 | |||
| 712 | /* | ||
| 713 | * We'd better turn off interrupts before | ||
| 714 | * we mess with the events-table.. | ||
| 715 | */ | ||
| 716 | |||
| 717 | spin_lock_irqsave(&events_lock, flags); | ||
| 718 | |||
| 719 | /* | ||
| 720 | * Play around with the interrupt mask to be able to | ||
| 721 | * give the events the generic pcmcia driver wants us to. | ||
| 722 | */ | ||
| 723 | |||
| 724 | e = &s->events[0]; | ||
| 725 | reg = 0; | ||
| 726 | |||
| 727 | if (state->csc_mask & SS_DETECT) { | ||
| 728 | e->eventbit = SS_DETECT; | ||
| 729 | reg |= e->regbit = (M8XX_PCMCIA_CD2(lsock) | ||
| 730 | | M8XX_PCMCIA_CD1(lsock)); | ||
| 731 | e++; | ||
| 732 | } | ||
| 733 | if (state->flags & SS_IOCARD) { | ||
| 734 | /* | ||
| 735 | * I/O card | ||
| 736 | */ | ||
| 737 | if (state->csc_mask & SS_STSCHG) { | ||
| 738 | e->eventbit = SS_STSCHG; | ||
| 739 | reg |= e->regbit = M8XX_PCMCIA_BVD1(lsock); | ||
| 740 | e++; | ||
| 741 | } | ||
| 742 | /* | ||
| 743 | * If io_irq is non-zero we should enable irq. | ||
| 744 | */ | ||
| 745 | if (state->io_irq) { | ||
| 746 | out_be32(M8XX_PGCRX(lsock), | ||
| 747 | in_be32(M8XX_PGCRX(lsock)) | | ||
| 748 | mk_int_int_mask(s->hwirq) << 24); | ||
| 749 | /* | ||
| 750 | * Strange thing here: | ||
| 751 | * The manual does not tell us which interrupt | ||
| 752 | * the sources generate. | ||
| 753 | * Anyhow, I found out that RDY_L generates IREQLVL. | ||
| 754 | * | ||
| 755 | * We use level triggerd interrupts, and they don't | ||
| 756 | * have to be cleared in PSCR in the interrupt handler. | ||
| 757 | */ | ||
| 758 | reg |= M8XX_PCMCIA_RDY_L(lsock); | ||
| 759 | } else | ||
| 760 | out_be32(M8XX_PGCRX(lsock), | ||
| 761 | in_be32(M8XX_PGCRX(lsock)) & 0x00ffffff); | ||
| 762 | } else { | ||
| 763 | /* | ||
| 764 | * Memory card | ||
| 765 | */ | ||
| 766 | if (state->csc_mask & SS_BATDEAD) { | ||
| 767 | e->eventbit = SS_BATDEAD; | ||
| 768 | reg |= e->regbit = M8XX_PCMCIA_BVD1(lsock); | ||
| 769 | e++; | ||
| 770 | } | ||
| 771 | if (state->csc_mask & SS_BATWARN) { | ||
| 772 | e->eventbit = SS_BATWARN; | ||
| 773 | reg |= e->regbit = M8XX_PCMCIA_BVD2(lsock); | ||
| 774 | e++; | ||
| 775 | } | ||
| 776 | /* What should I trigger on - low/high,raise,fall? */ | ||
| 777 | if (state->csc_mask & SS_READY) { | ||
| 778 | e->eventbit = SS_READY; | ||
| 779 | reg |= e->regbit = 0; //?? | ||
| 780 | e++; | ||
| 781 | } | ||
| 782 | } | ||
| 783 | |||
| 784 | e->regbit = 0; /* terminate list */ | ||
| 785 | |||
| 786 | /* | ||
| 787 | * Clear the status changed . | ||
| 788 | * Port A and Port B share the same port. | ||
| 789 | * Writing ones will clear the bits. | ||
| 790 | */ | ||
| 791 | |||
| 792 | out_be32(&pcmcia->pcmc_pscr, reg); | ||
| 793 | |||
| 794 | /* | ||
| 795 | * Write the mask. | ||
| 796 | * Port A and Port B share the same port. | ||
| 797 | * Need for read-modify-write. | ||
| 798 | * Ones will enable the interrupt. | ||
| 799 | */ | ||
| 800 | |||
| 801 | reg |= | ||
| 802 | in_be32(&pcmcia-> | ||
| 803 | pcmc_per) & (M8XX_PCMCIA_MASK(0) | M8XX_PCMCIA_MASK(1)); | ||
| 804 | out_be32(&pcmcia->pcmc_per, reg); | ||
| 805 | |||
| 806 | spin_unlock_irqrestore(&events_lock, flags); | ||
| 807 | |||
| 808 | /* copy the struct and modify the copy */ | ||
| 809 | |||
| 810 | s->state = *state; | ||
| 811 | |||
| 812 | return 0; | ||
| 813 | } | ||
| 814 | |||
| 815 | static int m8xx_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) | ||
| 816 | { | ||
| 817 | int lsock = container_of(sock, struct socket_info, socket)->slot; | ||
| 818 | |||
| 819 | struct socket_info *s = &socket[lsock]; | ||
| 820 | struct pcmcia_win *w; | ||
| 821 | unsigned int reg, winnr; | ||
| 822 | pcmconf8xx_t *pcmcia = s->pcmcia; | ||
| 823 | |||
| 824 | #define M8XX_SIZE (io->stop - io->start + 1) | ||
| 825 | #define M8XX_BASE (PCMCIA_IO_WIN_BASE + io->start) | ||
| 826 | |||
| 827 | pr_debug("m8xx_pcmcia: SetIOMap(%d, %d, %#2.2x, %d ns, " | ||
| 828 | "%#4.4llx-%#4.4llx)\n", lsock, io->map, io->flags, | ||
| 829 | io->speed, (unsigned long long)io->start, | ||
| 830 | (unsigned long long)io->stop); | ||
| 831 | |||
| 832 | if ((io->map >= PCMCIA_IO_WIN_NO) || (io->start > 0xffff) | ||
| 833 | || (io->stop > 0xffff) || (io->stop < io->start)) | ||
| 834 | return -EINVAL; | ||
| 835 | |||
| 836 | if ((reg = m8xx_get_graycode(M8XX_SIZE)) == -1) | ||
| 837 | return -EINVAL; | ||
| 838 | |||
| 839 | if (io->flags & MAP_ACTIVE) { | ||
| 840 | |||
| 841 | pr_debug("m8xx_pcmcia: io->flags & MAP_ACTIVE\n"); | ||
| 842 | |||
| 843 | winnr = (PCMCIA_MEM_WIN_NO * PCMCIA_SOCKETS_NO) | ||
| 844 | + (lsock * PCMCIA_IO_WIN_NO) + io->map; | ||
| 845 | |||
| 846 | /* setup registers */ | ||
| 847 | |||
| 848 | w = (void *)&pcmcia->pcmc_pbr0; | ||
| 849 | w += winnr; | ||
| 850 | |||
| 851 | out_be32(&w->or, 0); /* turn off window first */ | ||
| 852 | out_be32(&w->br, M8XX_BASE); | ||
| 853 | |||
| 854 | reg <<= 27; | ||
| 855 | reg |= M8XX_PCMCIA_POR_IO | (lsock << 2); | ||
| 856 | |||
| 857 | reg |= m8xx_get_speed(io->speed, 1, s->bus_freq); | ||
| 858 | |||
| 859 | if (io->flags & MAP_WRPROT) | ||
| 860 | reg |= M8XX_PCMCIA_POR_WRPROT; | ||
| 861 | |||
| 862 | /*if(io->flags & (MAP_16BIT | MAP_AUTOSZ)) */ | ||
| 863 | if (io->flags & MAP_16BIT) | ||
| 864 | reg |= M8XX_PCMCIA_POR_16BIT; | ||
| 865 | |||
| 866 | if (io->flags & MAP_ACTIVE) | ||
| 867 | reg |= M8XX_PCMCIA_POR_VALID; | ||
| 868 | |||
| 869 | out_be32(&w->or, reg); | ||
| 870 | |||
| 871 | pr_debug("m8xx_pcmcia: Socket %u: Mapped io window %u at " | ||
| 872 | "%#8.8x, OR = %#8.8x.\n", lsock, io->map, w->br, w->or); | ||
| 873 | } else { | ||
| 874 | /* shutdown IO window */ | ||
| 875 | winnr = (PCMCIA_MEM_WIN_NO * PCMCIA_SOCKETS_NO) | ||
| 876 | + (lsock * PCMCIA_IO_WIN_NO) + io->map; | ||
| 877 | |||
| 878 | /* setup registers */ | ||
| 879 | |||
| 880 | w = (void *)&pcmcia->pcmc_pbr0; | ||
| 881 | w += winnr; | ||
| 882 | |||
| 883 | out_be32(&w->or, 0); /* turn off window */ | ||
| 884 | out_be32(&w->br, 0); /* turn off base address */ | ||
| 885 | |||
| 886 | pr_debug("m8xx_pcmcia: Socket %u: Unmapped io window %u at " | ||
| 887 | "%#8.8x, OR = %#8.8x.\n", lsock, io->map, w->br, w->or); | ||
| 888 | } | ||
| 889 | |||
| 890 | /* copy the struct and modify the copy */ | ||
| 891 | s->io_win[io->map] = *io; | ||
| 892 | s->io_win[io->map].flags &= (MAP_WRPROT | MAP_16BIT | MAP_ACTIVE); | ||
| 893 | pr_debug("m8xx_pcmcia: SetIOMap exit\n"); | ||
| 894 | |||
| 895 | return 0; | ||
| 896 | } | ||
| 897 | |||
| 898 | static int m8xx_set_mem_map(struct pcmcia_socket *sock, | ||
| 899 | struct pccard_mem_map *mem) | ||
| 900 | { | ||
| 901 | int lsock = container_of(sock, struct socket_info, socket)->slot; | ||
| 902 | struct socket_info *s = &socket[lsock]; | ||
| 903 | struct pcmcia_win *w; | ||
| 904 | struct pccard_mem_map *old; | ||
| 905 | unsigned int reg, winnr; | ||
| 906 | pcmconf8xx_t *pcmcia = s->pcmcia; | ||
| 907 | |||
| 908 | pr_debug("m8xx_pcmcia: SetMemMap(%d, %d, %#2.2x, %d ns, " | ||
| 909 | "%#5.5llx, %#5.5x)\n", lsock, mem->map, mem->flags, | ||
| 910 | mem->speed, (unsigned long long)mem->static_start, | ||
| 911 | mem->card_start); | ||
| 912 | |||
| 913 | if ((mem->map >= PCMCIA_MEM_WIN_NO) | ||
| 914 | // || ((mem->s) >= PCMCIA_MEM_WIN_SIZE) | ||
| 915 | || (mem->card_start >= 0x04000000) | ||
| 916 | || (mem->static_start & 0xfff) /* 4KByte resolution */ | ||
| 917 | ||(mem->card_start & 0xfff)) | ||
| 918 | return -EINVAL; | ||
| 919 | |||
| 920 | if ((reg = m8xx_get_graycode(PCMCIA_MEM_WIN_SIZE)) == -1) { | ||
| 921 | printk("Cannot set size to 0x%08x.\n", PCMCIA_MEM_WIN_SIZE); | ||
| 922 | return -EINVAL; | ||
| 923 | } | ||
| 924 | reg <<= 27; | ||
| 925 | |||
| 926 | winnr = (lsock * PCMCIA_MEM_WIN_NO) + mem->map; | ||
| 927 | |||
| 928 | /* Setup the window in the pcmcia controller */ | ||
| 929 | |||
| 930 | w = (void *)&pcmcia->pcmc_pbr0; | ||
| 931 | w += winnr; | ||
| 932 | |||
| 933 | reg |= lsock << 2; | ||
| 934 | |||
| 935 | reg |= m8xx_get_speed(mem->speed, 0, s->bus_freq); | ||
| 936 | |||
| 937 | if (mem->flags & MAP_ATTRIB) | ||
| 938 | reg |= M8XX_PCMCIA_POR_ATTRMEM; | ||
| 939 | |||
| 940 | if (mem->flags & MAP_WRPROT) | ||
| 941 | reg |= M8XX_PCMCIA_POR_WRPROT; | ||
| 942 | |||
| 943 | if (mem->flags & MAP_16BIT) | ||
| 944 | reg |= M8XX_PCMCIA_POR_16BIT; | ||
| 945 | |||
| 946 | if (mem->flags & MAP_ACTIVE) | ||
| 947 | reg |= M8XX_PCMCIA_POR_VALID; | ||
| 948 | |||
| 949 | out_be32(&w->or, reg); | ||
| 950 | |||
| 951 | pr_debug("m8xx_pcmcia: Socket %u: Mapped memory window %u at %#8.8x, " | ||
| 952 | "OR = %#8.8x.\n", lsock, mem->map, w->br, w->or); | ||
| 953 | |||
| 954 | if (mem->flags & MAP_ACTIVE) { | ||
| 955 | /* get the new base address */ | ||
| 956 | mem->static_start = PCMCIA_MEM_WIN_BASE + | ||
| 957 | (PCMCIA_MEM_WIN_SIZE * winnr) | ||
| 958 | + mem->card_start; | ||
| 959 | } | ||
| 960 | |||
| 961 | pr_debug("m8xx_pcmcia: SetMemMap(%d, %d, %#2.2x, %d ns, " | ||
| 962 | "%#5.5llx, %#5.5x)\n", lsock, mem->map, mem->flags, | ||
| 963 | mem->speed, (unsigned long long)mem->static_start, | ||
| 964 | mem->card_start); | ||
| 965 | |||
| 966 | /* copy the struct and modify the copy */ | ||
| 967 | |||
| 968 | old = &s->mem_win[mem->map]; | ||
| 969 | |||
| 970 | *old = *mem; | ||
| 971 | old->flags &= (MAP_ATTRIB | MAP_WRPROT | MAP_16BIT | MAP_ACTIVE); | ||
| 972 | |||
| 973 | return 0; | ||
| 974 | } | ||
| 975 | |||
| 976 | static int m8xx_sock_init(struct pcmcia_socket *sock) | ||
| 977 | { | ||
| 978 | int i; | ||
| 979 | pccard_io_map io = { 0, 0, 0, 0, 1 }; | ||
| 980 | pccard_mem_map mem = { 0, 0, 0, 0, 0, 0 }; | ||
| 981 | |||
| 982 | pr_debug("m8xx_pcmcia: sock_init(%d)\n", s); | ||
| 983 | |||
| 984 | m8xx_set_socket(sock, &dead_socket); | ||
| 985 | for (i = 0; i < PCMCIA_IO_WIN_NO; i++) { | ||
| 986 | io.map = i; | ||
| 987 | m8xx_set_io_map(sock, &io); | ||
| 988 | } | ||
| 989 | for (i = 0; i < PCMCIA_MEM_WIN_NO; i++) { | ||
| 990 | mem.map = i; | ||
| 991 | m8xx_set_mem_map(sock, &mem); | ||
| 992 | } | ||
| 993 | |||
| 994 | return 0; | ||
| 995 | |||
| 996 | } | ||
| 997 | |||
| 998 | static int m8xx_sock_suspend(struct pcmcia_socket *sock) | ||
| 999 | { | ||
| 1000 | return m8xx_set_socket(sock, &dead_socket); | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | static struct pccard_operations m8xx_services = { | ||
| 1004 | .init = m8xx_sock_init, | ||
| 1005 | .suspend = m8xx_sock_suspend, | ||
| 1006 | .get_status = m8xx_get_status, | ||
| 1007 | .set_socket = m8xx_set_socket, | ||
| 1008 | .set_io_map = m8xx_set_io_map, | ||
| 1009 | .set_mem_map = m8xx_set_mem_map, | ||
| 1010 | }; | ||
| 1011 | |||
| 1012 | static int __init m8xx_probe(struct platform_device *ofdev) | ||
| 1013 | { | ||
| 1014 | struct pcmcia_win *w; | ||
| 1015 | unsigned int i, m, hwirq; | ||
| 1016 | pcmconf8xx_t *pcmcia; | ||
| 1017 | int status; | ||
| 1018 | struct device_node *np = ofdev->dev.of_node; | ||
| 1019 | |||
| 1020 | pcmcia_info("%s\n", version); | ||
| 1021 | |||
| 1022 | pcmcia = of_iomap(np, 0); | ||
| 1023 | if (pcmcia == NULL) | ||
| 1024 | return -EINVAL; | ||
| 1025 | |||
| 1026 | pcmcia_schlvl = irq_of_parse_and_map(np, 0); | ||
| 1027 | hwirq = irq_map[pcmcia_schlvl].hwirq; | ||
| 1028 | if (pcmcia_schlvl < 0) { | ||
| 1029 | iounmap(pcmcia); | ||
| 1030 | return -EINVAL; | ||
| 1031 | } | ||
| 1032 | |||
| 1033 | m8xx_pgcrx[0] = &pcmcia->pcmc_pgcra; | ||
| 1034 | m8xx_pgcrx[1] = &pcmcia->pcmc_pgcrb; | ||
| 1035 | |||
| 1036 | pcmcia_info(PCMCIA_BOARD_MSG " using " PCMCIA_SLOT_MSG | ||
| 1037 | " with IRQ %u (%d). \n", pcmcia_schlvl, hwirq); | ||
| 1038 | |||
| 1039 | /* Configure Status change interrupt */ | ||
| 1040 | |||
| 1041 | if (request_irq(pcmcia_schlvl, m8xx_interrupt, IRQF_SHARED, | ||
| 1042 | driver_name, socket)) { | ||
| 1043 | pcmcia_error("Cannot allocate IRQ %u for SCHLVL!\n", | ||
| 1044 | pcmcia_schlvl); | ||
| 1045 | iounmap(pcmcia); | ||
| 1046 | return -1; | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | w = (void *)&pcmcia->pcmc_pbr0; | ||
| 1050 | |||
| 1051 | out_be32(&pcmcia->pcmc_pscr, M8XX_PCMCIA_MASK(0) | M8XX_PCMCIA_MASK(1)); | ||
| 1052 | clrbits32(&pcmcia->pcmc_per, M8XX_PCMCIA_MASK(0) | M8XX_PCMCIA_MASK(1)); | ||
| 1053 | |||
| 1054 | /* connect interrupt and disable CxOE */ | ||
| 1055 | |||
| 1056 | out_be32(M8XX_PGCRX(0), | ||
| 1057 | M8XX_PGCRX_CXOE | (mk_int_int_mask(hwirq) << 16)); | ||
| 1058 | out_be32(M8XX_PGCRX(1), | ||
| 1059 | M8XX_PGCRX_CXOE | (mk_int_int_mask(hwirq) << 16)); | ||
| 1060 | |||
| 1061 | /* initialize the fixed memory windows */ | ||
| 1062 | |||
| 1063 | for (i = 0; i < PCMCIA_SOCKETS_NO; i++) { | ||
| 1064 | for (m = 0; m < PCMCIA_MEM_WIN_NO; m++) { | ||
| 1065 | out_be32(&w->br, PCMCIA_MEM_WIN_BASE + | ||
| 1066 | (PCMCIA_MEM_WIN_SIZE | ||
| 1067 | * (m + i * PCMCIA_MEM_WIN_NO))); | ||
| 1068 | |||
| 1069 | out_be32(&w->or, 0); /* set to not valid */ | ||
| 1070 | |||
| 1071 | w++; | ||
| 1072 | } | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | /* turn off voltage */ | ||
| 1076 | voltage_set(0, 0, 0); | ||
| 1077 | voltage_set(1, 0, 0); | ||
| 1078 | |||
| 1079 | /* Enable external hardware */ | ||
| 1080 | hardware_enable(0); | ||
| 1081 | hardware_enable(1); | ||
| 1082 | |||
| 1083 | for (i = 0; i < PCMCIA_SOCKETS_NO; i++) { | ||
| 1084 | socket[i].slot = i; | ||
| 1085 | socket[i].socket.owner = THIS_MODULE; | ||
| 1086 | socket[i].socket.features = | ||
| 1087 | SS_CAP_PCCARD | SS_CAP_MEM_ALIGN | SS_CAP_STATIC_MAP; | ||
| 1088 | socket[i].socket.irq_mask = 0x000; | ||
| 1089 | socket[i].socket.map_size = 0x1000; | ||
| 1090 | socket[i].socket.io_offset = 0; | ||
| 1091 | socket[i].socket.pci_irq = pcmcia_schlvl; | ||
| 1092 | socket[i].socket.ops = &m8xx_services; | ||
| 1093 | socket[i].socket.resource_ops = &pccard_iodyn_ops; | ||
| 1094 | socket[i].socket.cb_dev = NULL; | ||
| 1095 | socket[i].socket.dev.parent = &ofdev->dev; | ||
| 1096 | socket[i].pcmcia = pcmcia; | ||
| 1097 | socket[i].bus_freq = ppc_proc_freq; | ||
| 1098 | socket[i].hwirq = hwirq; | ||
| 1099 | |||
| 1100 | } | ||
| 1101 | |||
| 1102 | for (i = 0; i < PCMCIA_SOCKETS_NO; i++) { | ||
| 1103 | status = pcmcia_register_socket(&socket[i].socket); | ||
| 1104 | if (status < 0) | ||
| 1105 | pcmcia_error("Socket register failed\n"); | ||
| 1106 | } | ||
| 1107 | |||
| 1108 | return 0; | ||
| 1109 | } | ||
| 1110 | |||
| 1111 | static int m8xx_remove(struct platform_device *ofdev) | ||
| 1112 | { | ||
| 1113 | u32 m, i; | ||
| 1114 | struct pcmcia_win *w; | ||
| 1115 | pcmconf8xx_t *pcmcia = socket[0].pcmcia; | ||
| 1116 | |||
| 1117 | for (i = 0; i < PCMCIA_SOCKETS_NO; i++) { | ||
| 1118 | w = (void *)&pcmcia->pcmc_pbr0; | ||
| 1119 | |||
| 1120 | out_be32(&pcmcia->pcmc_pscr, M8XX_PCMCIA_MASK(i)); | ||
| 1121 | out_be32(&pcmcia->pcmc_per, | ||
| 1122 | in_be32(&pcmcia->pcmc_per) & ~M8XX_PCMCIA_MASK(i)); | ||
| 1123 | |||
| 1124 | /* turn off interrupt and disable CxOE */ | ||
| 1125 | out_be32(M8XX_PGCRX(i), M8XX_PGCRX_CXOE); | ||
| 1126 | |||
| 1127 | /* turn off memory windows */ | ||
| 1128 | for (m = 0; m < PCMCIA_MEM_WIN_NO; m++) { | ||
| 1129 | out_be32(&w->or, 0); /* set to not valid */ | ||
| 1130 | w++; | ||
| 1131 | } | ||
| 1132 | |||
| 1133 | /* turn off voltage */ | ||
| 1134 | voltage_set(i, 0, 0); | ||
| 1135 | |||
| 1136 | /* disable external hardware */ | ||
| 1137 | hardware_disable(i); | ||
| 1138 | } | ||
| 1139 | for (i = 0; i < PCMCIA_SOCKETS_NO; i++) | ||
| 1140 | pcmcia_unregister_socket(&socket[i].socket); | ||
| 1141 | iounmap(pcmcia); | ||
| 1142 | |||
| 1143 | free_irq(pcmcia_schlvl, NULL); | ||
| 1144 | |||
| 1145 | return 0; | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | static const struct of_device_id m8xx_pcmcia_match[] = { | ||
| 1149 | { | ||
| 1150 | .type = "pcmcia", | ||
| 1151 | .compatible = "fsl,pq-pcmcia", | ||
| 1152 | }, | ||
| 1153 | {}, | ||
| 1154 | }; | ||
| 1155 | |||
| 1156 | MODULE_DEVICE_TABLE(of, m8xx_pcmcia_match); | ||
| 1157 | |||
| 1158 | static struct platform_driver m8xx_pcmcia_driver = { | ||
| 1159 | .driver = { | ||
| 1160 | .name = driver_name, | ||
| 1161 | .owner = THIS_MODULE, | ||
| 1162 | .of_match_table = m8xx_pcmcia_match, | ||
| 1163 | }, | ||
| 1164 | .probe = m8xx_probe, | ||
| 1165 | .remove = m8xx_remove, | ||
| 1166 | }; | ||
| 1167 | |||
| 1168 | module_platform_driver(m8xx_pcmcia_driver); | ||
diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile index 72bfabc8629e..50e30bc75e85 100644 --- a/drivers/vfio/Makefile +++ b/drivers/vfio/Makefile | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | obj-$(CONFIG_VFIO) += vfio.o | 1 | obj-$(CONFIG_VFIO) += vfio.o |
| 2 | obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o | 2 | obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o |
| 3 | obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o | 3 | obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o |
| 4 | obj-$(CONFIG_EEH) += vfio_spapr_eeh.o | ||
| 4 | obj-$(CONFIG_VFIO_PCI) += pci/ | 5 | obj-$(CONFIG_VFIO_PCI) += pci/ |
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 010e0f8b8e4f..e2ee80f36e3e 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c | |||
| @@ -157,8 +157,10 @@ static void vfio_pci_release(void *device_data) | |||
| 157 | { | 157 | { |
| 158 | struct vfio_pci_device *vdev = device_data; | 158 | struct vfio_pci_device *vdev = device_data; |
| 159 | 159 | ||
| 160 | if (atomic_dec_and_test(&vdev->refcnt)) | 160 | if (atomic_dec_and_test(&vdev->refcnt)) { |
| 161 | vfio_spapr_pci_eeh_release(vdev->pdev); | ||
| 161 | vfio_pci_disable(vdev); | 162 | vfio_pci_disable(vdev); |
| 163 | } | ||
| 162 | 164 | ||
| 163 | module_put(THIS_MODULE); | 165 | module_put(THIS_MODULE); |
| 164 | } | 166 | } |
| @@ -166,19 +168,27 @@ static void vfio_pci_release(void *device_data) | |||
| 166 | static int vfio_pci_open(void *device_data) | 168 | static int vfio_pci_open(void *device_data) |
| 167 | { | 169 | { |
| 168 | struct vfio_pci_device *vdev = device_data; | 170 | struct vfio_pci_device *vdev = device_data; |
| 171 | int ret; | ||
| 169 | 172 | ||
| 170 | if (!try_module_get(THIS_MODULE)) | 173 | if (!try_module_get(THIS_MODULE)) |
| 171 | return -ENODEV; | 174 | return -ENODEV; |
| 172 | 175 | ||
| 173 | if (atomic_inc_return(&vdev->refcnt) == 1) { | 176 | if (atomic_inc_return(&vdev->refcnt) == 1) { |
| 174 | int ret = vfio_pci_enable(vdev); | 177 | ret = vfio_pci_enable(vdev); |
| 178 | if (ret) | ||
| 179 | goto error; | ||
| 180 | |||
| 181 | ret = vfio_spapr_pci_eeh_open(vdev->pdev); | ||
| 175 | if (ret) { | 182 | if (ret) { |
| 176 | module_put(THIS_MODULE); | 183 | vfio_pci_disable(vdev); |
| 177 | return ret; | 184 | goto error; |
| 178 | } | 185 | } |
| 179 | } | 186 | } |
| 180 | 187 | ||
| 181 | return 0; | 188 | return 0; |
| 189 | error: | ||
| 190 | module_put(THIS_MODULE); | ||
| 191 | return ret; | ||
| 182 | } | 192 | } |
| 183 | 193 | ||
| 184 | static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type) | 194 | static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type) |
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c index a84788ba662c..730b4ef3e0cc 100644 --- a/drivers/vfio/vfio_iommu_spapr_tce.c +++ b/drivers/vfio/vfio_iommu_spapr_tce.c | |||
| @@ -156,7 +156,16 @@ static long tce_iommu_ioctl(void *iommu_data, | |||
| 156 | 156 | ||
| 157 | switch (cmd) { | 157 | switch (cmd) { |
| 158 | case VFIO_CHECK_EXTENSION: | 158 | case VFIO_CHECK_EXTENSION: |
| 159 | return (arg == VFIO_SPAPR_TCE_IOMMU) ? 1 : 0; | 159 | switch (arg) { |
| 160 | case VFIO_SPAPR_TCE_IOMMU: | ||
| 161 | ret = 1; | ||
| 162 | break; | ||
| 163 | default: | ||
| 164 | ret = vfio_spapr_iommu_eeh_ioctl(NULL, cmd, arg); | ||
| 165 | break; | ||
| 166 | } | ||
| 167 | |||
| 168 | return (ret < 0) ? 0 : ret; | ||
| 160 | 169 | ||
| 161 | case VFIO_IOMMU_SPAPR_TCE_GET_INFO: { | 170 | case VFIO_IOMMU_SPAPR_TCE_GET_INFO: { |
| 162 | struct vfio_iommu_spapr_tce_info info; | 171 | struct vfio_iommu_spapr_tce_info info; |
| @@ -283,6 +292,12 @@ static long tce_iommu_ioctl(void *iommu_data, | |||
| 283 | tce_iommu_disable(container); | 292 | tce_iommu_disable(container); |
| 284 | mutex_unlock(&container->lock); | 293 | mutex_unlock(&container->lock); |
| 285 | return 0; | 294 | return 0; |
| 295 | case VFIO_EEH_PE_OP: | ||
| 296 | if (!container->tbl || !container->tbl->it_group) | ||
| 297 | return -ENODEV; | ||
| 298 | |||
| 299 | return vfio_spapr_iommu_eeh_ioctl(container->tbl->it_group, | ||
| 300 | cmd, arg); | ||
| 286 | } | 301 | } |
| 287 | 302 | ||
| 288 | return -ENOTTY; | 303 | return -ENOTTY; |
diff --git a/drivers/vfio/vfio_spapr_eeh.c b/drivers/vfio/vfio_spapr_eeh.c new file mode 100644 index 000000000000..f834b4ce1431 --- /dev/null +++ b/drivers/vfio/vfio_spapr_eeh.c | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | /* | ||
| 2 | * EEH functionality support for VFIO devices. The feature is only | ||
| 3 | * available on sPAPR compatible platforms. | ||
| 4 | * | ||
| 5 | * Copyright Gavin Shan, IBM Corporation 2014. | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/uaccess.h> | ||
| 13 | #include <linux/vfio.h> | ||
| 14 | #include <asm/eeh.h> | ||
| 15 | |||
| 16 | /* We might build address mapping here for "fast" path later */ | ||
| 17 | int vfio_spapr_pci_eeh_open(struct pci_dev *pdev) | ||
| 18 | { | ||
| 19 | return eeh_dev_open(pdev); | ||
| 20 | } | ||
| 21 | |||
| 22 | void vfio_spapr_pci_eeh_release(struct pci_dev *pdev) | ||
| 23 | { | ||
| 24 | eeh_dev_release(pdev); | ||
| 25 | } | ||
| 26 | |||
| 27 | long vfio_spapr_iommu_eeh_ioctl(struct iommu_group *group, | ||
| 28 | unsigned int cmd, unsigned long arg) | ||
| 29 | { | ||
| 30 | struct eeh_pe *pe; | ||
| 31 | struct vfio_eeh_pe_op op; | ||
| 32 | unsigned long minsz; | ||
| 33 | long ret = -EINVAL; | ||
| 34 | |||
| 35 | switch (cmd) { | ||
| 36 | case VFIO_CHECK_EXTENSION: | ||
| 37 | if (arg == VFIO_EEH) | ||
| 38 | ret = eeh_enabled() ? 1 : 0; | ||
| 39 | else | ||
| 40 | ret = 0; | ||
| 41 | break; | ||
| 42 | case VFIO_EEH_PE_OP: | ||
| 43 | pe = eeh_iommu_group_to_pe(group); | ||
| 44 | if (!pe) | ||
| 45 | return -ENODEV; | ||
| 46 | |||
| 47 | minsz = offsetofend(struct vfio_eeh_pe_op, op); | ||
| 48 | if (copy_from_user(&op, (void __user *)arg, minsz)) | ||
| 49 | return -EFAULT; | ||
| 50 | if (op.argsz < minsz || op.flags) | ||
| 51 | return -EINVAL; | ||
| 52 | |||
| 53 | switch (op.op) { | ||
| 54 | case VFIO_EEH_PE_DISABLE: | ||
| 55 | ret = eeh_pe_set_option(pe, EEH_OPT_DISABLE); | ||
| 56 | break; | ||
| 57 | case VFIO_EEH_PE_ENABLE: | ||
| 58 | ret = eeh_pe_set_option(pe, EEH_OPT_ENABLE); | ||
| 59 | break; | ||
| 60 | case VFIO_EEH_PE_UNFREEZE_IO: | ||
| 61 | ret = eeh_pe_set_option(pe, EEH_OPT_THAW_MMIO); | ||
| 62 | break; | ||
| 63 | case VFIO_EEH_PE_UNFREEZE_DMA: | ||
| 64 | ret = eeh_pe_set_option(pe, EEH_OPT_THAW_DMA); | ||
| 65 | break; | ||
| 66 | case VFIO_EEH_PE_GET_STATE: | ||
| 67 | ret = eeh_pe_get_state(pe); | ||
| 68 | break; | ||
| 69 | case VFIO_EEH_PE_RESET_DEACTIVATE: | ||
| 70 | ret = eeh_pe_reset(pe, EEH_RESET_DEACTIVATE); | ||
| 71 | break; | ||
| 72 | case VFIO_EEH_PE_RESET_HOT: | ||
| 73 | ret = eeh_pe_reset(pe, EEH_RESET_HOT); | ||
| 74 | break; | ||
| 75 | case VFIO_EEH_PE_RESET_FUNDAMENTAL: | ||
| 76 | ret = eeh_pe_reset(pe, EEH_RESET_FUNDAMENTAL); | ||
| 77 | break; | ||
| 78 | case VFIO_EEH_PE_CONFIGURE: | ||
| 79 | ret = eeh_pe_configure(pe); | ||
| 80 | break; | ||
| 81 | default: | ||
| 82 | ret = -EINVAL; | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | return ret; | ||
| 87 | } | ||
