1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright 2016-17 IBM Corp. 4 */ 5 6 #define pr_fmt(fmt) "vas: " fmt 7 8 #include <linux/module.h> 9 #include <linux/kernel.h> 10 #include <linux/export.h> 11 #include <linux/types.h> 12 #include <linux/slab.h> 13 #include <linux/platform_device.h> 14 #include <linux/of_platform.h> 15 #include <linux/of_address.h> 16 #include <linux/of.h> 17 #include <asm/prom.h> 18 19 #include "vas.h" 20 21 DEFINE_MUTEX(vas_mutex); 22 static LIST_HEAD(vas_instances); 23 24 static DEFINE_PER_CPU(int, cpu_vas_id); 25 26 static int init_vas_instance(struct platform_device *pdev) 27 { 28 int rc, cpu, vasid; 29 struct resource *res; 30 struct vas_instance *vinst; 31 struct device_node *dn = pdev->dev.of_node; 32 33 rc = of_property_read_u32(dn, "ibm,vas-id", &vasid); 34 if (rc) { 35 pr_err("No ibm,vas-id property for %s?\n", pdev->name); 36 return -ENODEV; 37 } 38 39 if (pdev->num_resources != 4) { 40 pr_err("Unexpected DT configuration for [%s, %d]\n", 41 pdev->name, vasid); 42 return -ENODEV; 43 } 44 45 vinst = kzalloc(sizeof(*vinst), GFP_KERNEL); 46 if (!vinst) 47 return -ENOMEM; 48 49 INIT_LIST_HEAD(&vinst->node); 50 ida_init(&vinst->ida); 51 mutex_init(&vinst->mutex); 52 vinst->vas_id = vasid; 53 vinst->pdev = pdev; 54 55 res = &pdev->resource[0]; 56 vinst->hvwc_bar_start = res->start; 57 58 res = &pdev->resource[1]; 59 vinst->uwc_bar_start = res->start; 60 61 res = &pdev->resource[2]; 62 vinst->paste_base_addr = res->start; 63 64 res = &pdev->resource[3]; 65 if (res->end > 62) { 66 pr_err("Bad 'paste_win_id_shift' in DT, %llx\n", res->end); 67 goto free_vinst; 68 } 69 70 vinst->paste_win_id_shift = 63 - res->end; 71 72 pr_devel("Initialized instance [%s, %d], paste_base 0x%llx, " 73 "paste_win_id_shift 0x%llx\n", pdev->name, vasid, 74 vinst->paste_base_addr, vinst->paste_win_id_shift); 75 76 for_each_possible_cpu(cpu) { 77 if (cpu_to_chip_id(cpu) == of_get_ibm_chip_id(dn)) 78 per_cpu(cpu_vas_id, cpu) = vasid; 79 } 80 81 mutex_lock(&vas_mutex); 82 list_add(&vinst->node, &vas_instances); 83 mutex_unlock(&vas_mutex); 84 85 vas_instance_init_dbgdir(vinst); 86 87 dev_set_drvdata(&pdev->dev, vinst); 88 89 return 0; 90 91 free_vinst: 92 kfree(vinst); 93 return -ENODEV; 94 95 } 96 97 /* 98 * Although this is read/used multiple times, it is written to only 99 * during initialization. 100 */ 101 struct vas_instance *find_vas_instance(int vasid) 102 { 103 struct list_head *ent; 104 struct vas_instance *vinst; 105 106 mutex_lock(&vas_mutex); 107 108 if (vasid == -1) 109 vasid = per_cpu(cpu_vas_id, smp_processor_id()); 110 111 list_for_each(ent, &vas_instances) { 112 vinst = list_entry(ent, struct vas_instance, node); 113 if (vinst->vas_id == vasid) { 114 mutex_unlock(&vas_mutex); 115 return vinst; 116 } 117 } 118 mutex_unlock(&vas_mutex); 119 120 pr_devel("Instance %d not found\n", vasid); 121 return NULL; 122 } 123 124 int chip_to_vas_id(int chipid) 125 { 126 int cpu; 127 128 for_each_possible_cpu(cpu) { 129 if (cpu_to_chip_id(cpu) == chipid) 130 return per_cpu(cpu_vas_id, cpu); 131 } 132 return -1; 133 } 134 EXPORT_SYMBOL(chip_to_vas_id); 135 136 static int vas_probe(struct platform_device *pdev) 137 { 138 return init_vas_instance(pdev); 139 } 140 141 static const struct of_device_id powernv_vas_match[] = { 142 { .compatible = "ibm,vas",}, 143 {}, 144 }; 145 146 static struct platform_driver vas_driver = { 147 .driver = { 148 .name = "vas", 149 .of_match_table = powernv_vas_match, 150 }, 151 .probe = vas_probe, 152 }; 153 154 static int __init vas_init(void) 155 { 156 int found = 0; 157 struct device_node *dn; 158 159 platform_driver_register(&vas_driver); 160 161 for_each_compatible_node(dn, NULL, "ibm,vas") { 162 of_platform_device_create(dn, NULL, NULL); 163 found++; 164 } 165 166 if (!found) { 167 platform_driver_unregister(&vas_driver); 168 return -ENODEV; 169 } 170 171 pr_devel("Found %d instances\n", found); 172 173 return 0; 174 } 175 device_initcall(vas_init); 176