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