162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#define pr_fmt(fmt) "ipmi_hardcode: " fmt 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/moduleparam.h> 662306a36Sopenharmony_ci#include <linux/platform_device.h> 762306a36Sopenharmony_ci#include "ipmi_si.h" 862306a36Sopenharmony_ci#include "ipmi_plat_data.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* 1162306a36Sopenharmony_ci * There can be 4 IO ports passed in (with or without IRQs), 4 addresses, 1262306a36Sopenharmony_ci * a default IO port, and 1 ACPI/SPMI address. That sets SI_MAX_DRIVERS. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define SI_MAX_PARMS 4 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define MAX_SI_TYPE_STR 30 1862306a36Sopenharmony_cistatic char si_type_str[MAX_SI_TYPE_STR] __initdata; 1962306a36Sopenharmony_cistatic unsigned long addrs[SI_MAX_PARMS]; 2062306a36Sopenharmony_cistatic unsigned int num_addrs; 2162306a36Sopenharmony_cistatic unsigned int ports[SI_MAX_PARMS]; 2262306a36Sopenharmony_cistatic unsigned int num_ports; 2362306a36Sopenharmony_cistatic int irqs[SI_MAX_PARMS] __initdata; 2462306a36Sopenharmony_cistatic unsigned int num_irqs __initdata; 2562306a36Sopenharmony_cistatic int regspacings[SI_MAX_PARMS] __initdata; 2662306a36Sopenharmony_cistatic unsigned int num_regspacings __initdata; 2762306a36Sopenharmony_cistatic int regsizes[SI_MAX_PARMS] __initdata; 2862306a36Sopenharmony_cistatic unsigned int num_regsizes __initdata; 2962306a36Sopenharmony_cistatic int regshifts[SI_MAX_PARMS] __initdata; 3062306a36Sopenharmony_cistatic unsigned int num_regshifts __initdata; 3162306a36Sopenharmony_cistatic int slave_addrs[SI_MAX_PARMS] __initdata; 3262306a36Sopenharmony_cistatic unsigned int num_slave_addrs __initdata; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cimodule_param_string(type, si_type_str, MAX_SI_TYPE_STR, 0); 3562306a36Sopenharmony_ciMODULE_PARM_DESC(type, 3662306a36Sopenharmony_ci "Defines the type of each interface, each interface separated by commas. The types are 'kcs', 'smic', and 'bt'. For example si_type=kcs,bt will set the first interface to kcs and the second to bt"); 3762306a36Sopenharmony_cimodule_param_hw_array(addrs, ulong, iomem, &num_addrs, 0); 3862306a36Sopenharmony_ciMODULE_PARM_DESC(addrs, 3962306a36Sopenharmony_ci "Sets the memory address of each interface, the addresses separated by commas. Only use if an interface is in memory. Otherwise, set it to zero or leave it blank."); 4062306a36Sopenharmony_cimodule_param_hw_array(ports, uint, ioport, &num_ports, 0); 4162306a36Sopenharmony_ciMODULE_PARM_DESC(ports, 4262306a36Sopenharmony_ci "Sets the port address of each interface, the addresses separated by commas. Only use if an interface is a port. Otherwise, set it to zero or leave it blank."); 4362306a36Sopenharmony_cimodule_param_hw_array(irqs, int, irq, &num_irqs, 0); 4462306a36Sopenharmony_ciMODULE_PARM_DESC(irqs, 4562306a36Sopenharmony_ci "Sets the interrupt of each interface, the addresses separated by commas. Only use if an interface has an interrupt. Otherwise, set it to zero or leave it blank."); 4662306a36Sopenharmony_cimodule_param_hw_array(regspacings, int, other, &num_regspacings, 0); 4762306a36Sopenharmony_ciMODULE_PARM_DESC(regspacings, 4862306a36Sopenharmony_ci "The number of bytes between the start address and each successive register used by the interface. For instance, if the start address is 0xca2 and the spacing is 2, then the second address is at 0xca4. Defaults to 1."); 4962306a36Sopenharmony_cimodule_param_hw_array(regsizes, int, other, &num_regsizes, 0); 5062306a36Sopenharmony_ciMODULE_PARM_DESC(regsizes, 5162306a36Sopenharmony_ci "The size of the specific IPMI register in bytes. This should generally be 1, 2, 4, or 8 for an 8-bit, 16-bit, 32-bit, or 64-bit register. Use this if you the 8-bit IPMI register has to be read from a larger register."); 5262306a36Sopenharmony_cimodule_param_hw_array(regshifts, int, other, &num_regshifts, 0); 5362306a36Sopenharmony_ciMODULE_PARM_DESC(regshifts, 5462306a36Sopenharmony_ci "The amount to shift the data read from the. IPMI register, in bits. For instance, if the data is read from a 32-bit word and the IPMI data is in bit 8-15, then the shift would be 8"); 5562306a36Sopenharmony_cimodule_param_hw_array(slave_addrs, int, other, &num_slave_addrs, 0); 5662306a36Sopenharmony_ciMODULE_PARM_DESC(slave_addrs, 5762306a36Sopenharmony_ci "Set the default IPMB slave address for the controller. Normally this is 0x20, but can be overridden by this parm. This is an array indexed by interface number."); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void __init ipmi_hardcode_init_one(const char *si_type_str, 6062306a36Sopenharmony_ci unsigned int i, 6162306a36Sopenharmony_ci unsigned long addr, 6262306a36Sopenharmony_ci enum ipmi_addr_space addr_space) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct ipmi_plat_data p; 6562306a36Sopenharmony_ci int t; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci memset(&p, 0, sizeof(p)); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci p.iftype = IPMI_PLAT_IF_SI; 7062306a36Sopenharmony_ci if (!si_type_str || !*si_type_str) { 7162306a36Sopenharmony_ci p.type = SI_KCS; 7262306a36Sopenharmony_ci } else { 7362306a36Sopenharmony_ci t = match_string(si_to_str, -1, si_type_str); 7462306a36Sopenharmony_ci if (t < 0) { 7562306a36Sopenharmony_ci pr_warn("Interface type specified for interface %d, was invalid: %s\n", 7662306a36Sopenharmony_ci i, si_type_str); 7762306a36Sopenharmony_ci return; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci p.type = t; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci p.regsize = regsizes[i]; 8362306a36Sopenharmony_ci p.slave_addr = slave_addrs[i]; 8462306a36Sopenharmony_ci p.addr_source = SI_HARDCODED; 8562306a36Sopenharmony_ci p.regshift = regshifts[i]; 8662306a36Sopenharmony_ci p.regsize = regsizes[i]; 8762306a36Sopenharmony_ci p.addr = addr; 8862306a36Sopenharmony_ci p.space = addr_space; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci ipmi_platform_add("hardcode-ipmi-si", i, &p); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_civoid __init ipmi_hardcode_init(void) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci unsigned int i; 9662306a36Sopenharmony_ci char *str; 9762306a36Sopenharmony_ci char *si_type[SI_MAX_PARMS]; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci memset(si_type, 0, sizeof(si_type)); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Parse out the si_type string into its components. */ 10262306a36Sopenharmony_ci str = si_type_str; 10362306a36Sopenharmony_ci if (*str != '\0') { 10462306a36Sopenharmony_ci for (i = 0; (i < SI_MAX_PARMS) && (*str != '\0'); i++) { 10562306a36Sopenharmony_ci si_type[i] = str; 10662306a36Sopenharmony_ci str = strchr(str, ','); 10762306a36Sopenharmony_ci if (str) { 10862306a36Sopenharmony_ci *str = '\0'; 10962306a36Sopenharmony_ci str++; 11062306a36Sopenharmony_ci } else { 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci for (i = 0; i < SI_MAX_PARMS; i++) { 11762306a36Sopenharmony_ci if (i < num_ports && ports[i]) 11862306a36Sopenharmony_ci ipmi_hardcode_init_one(si_type[i], i, ports[i], 11962306a36Sopenharmony_ci IPMI_IO_ADDR_SPACE); 12062306a36Sopenharmony_ci if (i < num_addrs && addrs[i]) 12162306a36Sopenharmony_ci ipmi_hardcode_init_one(si_type[i], i, addrs[i], 12262306a36Sopenharmony_ci IPMI_MEM_ADDR_SPACE); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_civoid ipmi_si_hardcode_exit(void) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci ipmi_remove_platform_device_by_name("hardcode-ipmi-si"); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* 13362306a36Sopenharmony_ci * Returns true of the given address exists as a hardcoded address, 13462306a36Sopenharmony_ci * false if not. 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ciint ipmi_si_hardcode_match(int addr_space, unsigned long addr) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci unsigned int i; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (addr_space == IPMI_IO_ADDR_SPACE) { 14162306a36Sopenharmony_ci for (i = 0; i < num_ports; i++) { 14262306a36Sopenharmony_ci if (ports[i] == addr) 14362306a36Sopenharmony_ci return 1; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci } else { 14662306a36Sopenharmony_ci for (i = 0; i < num_addrs; i++) { 14762306a36Sopenharmony_ci if (addrs[i] == addr) 14862306a36Sopenharmony_ci return 1; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci} 154