18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "ipmi_hardcode: " fmt 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 68c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 78c2ecf20Sopenharmony_ci#include "ipmi_si.h" 88c2ecf20Sopenharmony_ci#include "ipmi_plat_data.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* 118c2ecf20Sopenharmony_ci * There can be 4 IO ports passed in (with or without IRQs), 4 addresses, 128c2ecf20Sopenharmony_ci * a default IO port, and 1 ACPI/SPMI address. That sets SI_MAX_DRIVERS. 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define SI_MAX_PARMS 4 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define MAX_SI_TYPE_STR 30 188c2ecf20Sopenharmony_cistatic char si_type_str[MAX_SI_TYPE_STR] __initdata; 198c2ecf20Sopenharmony_cistatic unsigned long addrs[SI_MAX_PARMS]; 208c2ecf20Sopenharmony_cistatic unsigned int num_addrs; 218c2ecf20Sopenharmony_cistatic unsigned int ports[SI_MAX_PARMS]; 228c2ecf20Sopenharmony_cistatic unsigned int num_ports; 238c2ecf20Sopenharmony_cistatic int irqs[SI_MAX_PARMS] __initdata; 248c2ecf20Sopenharmony_cistatic unsigned int num_irqs __initdata; 258c2ecf20Sopenharmony_cistatic int regspacings[SI_MAX_PARMS] __initdata; 268c2ecf20Sopenharmony_cistatic unsigned int num_regspacings __initdata; 278c2ecf20Sopenharmony_cistatic int regsizes[SI_MAX_PARMS] __initdata; 288c2ecf20Sopenharmony_cistatic unsigned int num_regsizes __initdata; 298c2ecf20Sopenharmony_cistatic int regshifts[SI_MAX_PARMS] __initdata; 308c2ecf20Sopenharmony_cistatic unsigned int num_regshifts __initdata; 318c2ecf20Sopenharmony_cistatic int slave_addrs[SI_MAX_PARMS] __initdata; 328c2ecf20Sopenharmony_cistatic unsigned int num_slave_addrs __initdata; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cimodule_param_string(type, si_type_str, MAX_SI_TYPE_STR, 0); 358c2ecf20Sopenharmony_ciMODULE_PARM_DESC(type, "Defines the type of each interface, each" 368c2ecf20Sopenharmony_ci " interface separated by commas. The types are 'kcs'," 378c2ecf20Sopenharmony_ci " 'smic', and 'bt'. For example si_type=kcs,bt will set" 388c2ecf20Sopenharmony_ci " the first interface to kcs and the second to bt"); 398c2ecf20Sopenharmony_cimodule_param_hw_array(addrs, ulong, iomem, &num_addrs, 0); 408c2ecf20Sopenharmony_ciMODULE_PARM_DESC(addrs, "Sets the memory address of each interface, the" 418c2ecf20Sopenharmony_ci " addresses separated by commas. Only use if an interface" 428c2ecf20Sopenharmony_ci " is in memory. Otherwise, set it to zero or leave" 438c2ecf20Sopenharmony_ci " it blank."); 448c2ecf20Sopenharmony_cimodule_param_hw_array(ports, uint, ioport, &num_ports, 0); 458c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ports, "Sets the port address of each interface, the" 468c2ecf20Sopenharmony_ci " addresses separated by commas. Only use if an interface" 478c2ecf20Sopenharmony_ci " is a port. Otherwise, set it to zero or leave" 488c2ecf20Sopenharmony_ci " it blank."); 498c2ecf20Sopenharmony_cimodule_param_hw_array(irqs, int, irq, &num_irqs, 0); 508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irqs, "Sets the interrupt of each interface, the" 518c2ecf20Sopenharmony_ci " addresses separated by commas. Only use if an interface" 528c2ecf20Sopenharmony_ci " has an interrupt. Otherwise, set it to zero or leave" 538c2ecf20Sopenharmony_ci " it blank."); 548c2ecf20Sopenharmony_cimodule_param_hw_array(regspacings, int, other, &num_regspacings, 0); 558c2ecf20Sopenharmony_ciMODULE_PARM_DESC(regspacings, "The number of bytes between the start address" 568c2ecf20Sopenharmony_ci " and each successive register used by the interface. For" 578c2ecf20Sopenharmony_ci " instance, if the start address is 0xca2 and the spacing" 588c2ecf20Sopenharmony_ci " is 2, then the second address is at 0xca4. Defaults" 598c2ecf20Sopenharmony_ci " to 1."); 608c2ecf20Sopenharmony_cimodule_param_hw_array(regsizes, int, other, &num_regsizes, 0); 618c2ecf20Sopenharmony_ciMODULE_PARM_DESC(regsizes, "The size of the specific IPMI register in bytes." 628c2ecf20Sopenharmony_ci " This should generally be 1, 2, 4, or 8 for an 8-bit," 638c2ecf20Sopenharmony_ci " 16-bit, 32-bit, or 64-bit register. Use this if you" 648c2ecf20Sopenharmony_ci " the 8-bit IPMI register has to be read from a larger" 658c2ecf20Sopenharmony_ci " register."); 668c2ecf20Sopenharmony_cimodule_param_hw_array(regshifts, int, other, &num_regshifts, 0); 678c2ecf20Sopenharmony_ciMODULE_PARM_DESC(regshifts, "The amount to shift the data read from the." 688c2ecf20Sopenharmony_ci " IPMI register, in bits. For instance, if the data" 698c2ecf20Sopenharmony_ci " is read from a 32-bit word and the IPMI data is in" 708c2ecf20Sopenharmony_ci " bit 8-15, then the shift would be 8"); 718c2ecf20Sopenharmony_cimodule_param_hw_array(slave_addrs, int, other, &num_slave_addrs, 0); 728c2ecf20Sopenharmony_ciMODULE_PARM_DESC(slave_addrs, "Set the default IPMB slave address for" 738c2ecf20Sopenharmony_ci " the controller. Normally this is 0x20, but can be" 748c2ecf20Sopenharmony_ci " overridden by this parm. This is an array indexed" 758c2ecf20Sopenharmony_ci " by interface number."); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic void __init ipmi_hardcode_init_one(const char *si_type_str, 788c2ecf20Sopenharmony_ci unsigned int i, 798c2ecf20Sopenharmony_ci unsigned long addr, 808c2ecf20Sopenharmony_ci enum ipmi_addr_space addr_space) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct ipmi_plat_data p; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci memset(&p, 0, sizeof(p)); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci p.iftype = IPMI_PLAT_IF_SI; 878c2ecf20Sopenharmony_ci if (!si_type_str || !*si_type_str || strcmp(si_type_str, "kcs") == 0) { 888c2ecf20Sopenharmony_ci p.type = SI_KCS; 898c2ecf20Sopenharmony_ci } else if (strcmp(si_type_str, "smic") == 0) { 908c2ecf20Sopenharmony_ci p.type = SI_SMIC; 918c2ecf20Sopenharmony_ci } else if (strcmp(si_type_str, "bt") == 0) { 928c2ecf20Sopenharmony_ci p.type = SI_BT; 938c2ecf20Sopenharmony_ci } else if (strcmp(si_type_str, "invalid") == 0) { 948c2ecf20Sopenharmony_ci /* 958c2ecf20Sopenharmony_ci * Allow a firmware-specified interface to be 968c2ecf20Sopenharmony_ci * disabled. 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci p.type = SI_TYPE_INVALID; 998c2ecf20Sopenharmony_ci } else { 1008c2ecf20Sopenharmony_ci pr_warn("Interface type specified for interface %d, was invalid: %s\n", 1018c2ecf20Sopenharmony_ci i, si_type_str); 1028c2ecf20Sopenharmony_ci return; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci p.regsize = regsizes[i]; 1068c2ecf20Sopenharmony_ci p.slave_addr = slave_addrs[i]; 1078c2ecf20Sopenharmony_ci p.addr_source = SI_HARDCODED; 1088c2ecf20Sopenharmony_ci p.regshift = regshifts[i]; 1098c2ecf20Sopenharmony_ci p.regsize = regsizes[i]; 1108c2ecf20Sopenharmony_ci p.addr = addr; 1118c2ecf20Sopenharmony_ci p.space = addr_space; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci ipmi_platform_add("hardcode-ipmi-si", i, &p); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_civoid __init ipmi_hardcode_init(void) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci unsigned int i; 1198c2ecf20Sopenharmony_ci char *str; 1208c2ecf20Sopenharmony_ci char *si_type[SI_MAX_PARMS]; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci memset(si_type, 0, sizeof(si_type)); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* Parse out the si_type string into its components. */ 1258c2ecf20Sopenharmony_ci str = si_type_str; 1268c2ecf20Sopenharmony_ci if (*str != '\0') { 1278c2ecf20Sopenharmony_ci for (i = 0; (i < SI_MAX_PARMS) && (*str != '\0'); i++) { 1288c2ecf20Sopenharmony_ci si_type[i] = str; 1298c2ecf20Sopenharmony_ci str = strchr(str, ','); 1308c2ecf20Sopenharmony_ci if (str) { 1318c2ecf20Sopenharmony_ci *str = '\0'; 1328c2ecf20Sopenharmony_ci str++; 1338c2ecf20Sopenharmony_ci } else { 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci for (i = 0; i < SI_MAX_PARMS; i++) { 1408c2ecf20Sopenharmony_ci if (i < num_ports && ports[i]) 1418c2ecf20Sopenharmony_ci ipmi_hardcode_init_one(si_type[i], i, ports[i], 1428c2ecf20Sopenharmony_ci IPMI_IO_ADDR_SPACE); 1438c2ecf20Sopenharmony_ci if (i < num_addrs && addrs[i]) 1448c2ecf20Sopenharmony_ci ipmi_hardcode_init_one(si_type[i], i, addrs[i], 1458c2ecf20Sopenharmony_ci IPMI_MEM_ADDR_SPACE); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_civoid ipmi_si_hardcode_exit(void) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci ipmi_remove_platform_device_by_name("hardcode-ipmi-si"); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* 1568c2ecf20Sopenharmony_ci * Returns true of the given address exists as a hardcoded address, 1578c2ecf20Sopenharmony_ci * false if not. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ciint ipmi_si_hardcode_match(int addr_space, unsigned long addr) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci unsigned int i; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (addr_space == IPMI_IO_ADDR_SPACE) { 1648c2ecf20Sopenharmony_ci for (i = 0; i < num_ports; i++) { 1658c2ecf20Sopenharmony_ci if (ports[i] == addr) 1668c2ecf20Sopenharmony_ci return 1; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci } else { 1698c2ecf20Sopenharmony_ci for (i = 0; i < num_addrs; i++) { 1708c2ecf20Sopenharmony_ci if (addrs[i] == addr) 1718c2ecf20Sopenharmony_ci return 1; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci} 177