1243ac210SCorey Minyard // SPDX-License-Identifier: GPL-2.0+
27a453308SCorey Minyard 
325880f7dSJoe Perches #define pr_fmt(fmt) "ipmi_hardcode: " fmt
425880f7dSJoe Perches 
57a453308SCorey Minyard #include <linux/moduleparam.h>
641b766d6SCorey Minyard #include <linux/platform_device.h>
77a453308SCorey Minyard #include "ipmi_si.h"
83cd83bacSCorey Minyard #include "ipmi_plat_data.h"
97a453308SCorey Minyard 
107a453308SCorey Minyard /*
117a453308SCorey Minyard  * There can be 4 IO ports passed in (with or without IRQs), 4 addresses,
127a453308SCorey Minyard  * a default IO port, and 1 ACPI/SPMI address.  That sets SI_MAX_DRIVERS.
137a453308SCorey Minyard  */
147a453308SCorey Minyard 
157a453308SCorey Minyard #define SI_MAX_PARMS 4
167a453308SCorey Minyard 
177a453308SCorey Minyard #define MAX_SI_TYPE_STR 30
1841b766d6SCorey Minyard static char          si_type_str[MAX_SI_TYPE_STR] __initdata;
197a453308SCorey Minyard static unsigned long addrs[SI_MAX_PARMS];
207a453308SCorey Minyard static unsigned int num_addrs;
217a453308SCorey Minyard static unsigned int  ports[SI_MAX_PARMS];
227a453308SCorey Minyard static unsigned int num_ports;
2341b766d6SCorey Minyard static int           irqs[SI_MAX_PARMS] __initdata;
2441b766d6SCorey Minyard static unsigned int num_irqs __initdata;
2541b766d6SCorey Minyard static int           regspacings[SI_MAX_PARMS] __initdata;
2641b766d6SCorey Minyard static unsigned int num_regspacings __initdata;
2741b766d6SCorey Minyard static int           regsizes[SI_MAX_PARMS] __initdata;
2841b766d6SCorey Minyard static unsigned int num_regsizes __initdata;
2941b766d6SCorey Minyard static int           regshifts[SI_MAX_PARMS] __initdata;
3041b766d6SCorey Minyard static unsigned int num_regshifts __initdata;
3141b766d6SCorey Minyard static int slave_addrs[SI_MAX_PARMS] __initdata;
3241b766d6SCorey Minyard static unsigned int num_slave_addrs __initdata;
337a453308SCorey Minyard 
347a453308SCorey Minyard module_param_string(type, si_type_str, MAX_SI_TYPE_STR, 0);
3507cbd87bSAndy Shevchenko MODULE_PARM_DESC(type,
3607cbd87bSAndy Shevchenko 		 "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");
377a453308SCorey Minyard module_param_hw_array(addrs, ulong, iomem, &num_addrs, 0);
3807cbd87bSAndy Shevchenko MODULE_PARM_DESC(addrs,
3907cbd87bSAndy Shevchenko 		 "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.");
407a453308SCorey Minyard module_param_hw_array(ports, uint, ioport, &num_ports, 0);
4107cbd87bSAndy Shevchenko MODULE_PARM_DESC(ports,
4207cbd87bSAndy Shevchenko 		 "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.");
437a453308SCorey Minyard module_param_hw_array(irqs, int, irq, &num_irqs, 0);
4407cbd87bSAndy Shevchenko MODULE_PARM_DESC(irqs,
4507cbd87bSAndy Shevchenko 		 "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.");
467a453308SCorey Minyard module_param_hw_array(regspacings, int, other, &num_regspacings, 0);
4707cbd87bSAndy Shevchenko MODULE_PARM_DESC(regspacings,
4807cbd87bSAndy Shevchenko 		 "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.");
497a453308SCorey Minyard module_param_hw_array(regsizes, int, other, &num_regsizes, 0);
5007cbd87bSAndy Shevchenko MODULE_PARM_DESC(regsizes,
5107cbd87bSAndy Shevchenko 		 "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.");
527a453308SCorey Minyard module_param_hw_array(regshifts, int, other, &num_regshifts, 0);
5307cbd87bSAndy Shevchenko MODULE_PARM_DESC(regshifts,
5407cbd87bSAndy Shevchenko 		 "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");
557a453308SCorey Minyard module_param_hw_array(slave_addrs, int, other, &num_slave_addrs, 0);
5607cbd87bSAndy Shevchenko MODULE_PARM_DESC(slave_addrs,
5707cbd87bSAndy Shevchenko 		 "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.");
587a453308SCorey Minyard 
ipmi_hardcode_init_one(const char * si_type_str,unsigned int i,unsigned long addr,enum ipmi_addr_space addr_space)5941b766d6SCorey Minyard static void __init ipmi_hardcode_init_one(const char *si_type_str,
6041b766d6SCorey Minyard 					  unsigned int i,
6141b766d6SCorey Minyard 					  unsigned long addr,
623cd83bacSCorey Minyard 					  enum ipmi_addr_space addr_space)
637a453308SCorey Minyard {
643cd83bacSCorey Minyard 	struct ipmi_plat_data p;
6559cdb2e7SAndy Shevchenko 	int t;
6641b766d6SCorey Minyard 
673cd83bacSCorey Minyard 	memset(&p, 0, sizeof(p));
6841b766d6SCorey Minyard 
69d7323638SCorey Minyard 	p.iftype = IPMI_PLAT_IF_SI;
7059cdb2e7SAndy Shevchenko 	if (!si_type_str || !*si_type_str) {
713cd83bacSCorey Minyard 		p.type = SI_KCS;
7241b766d6SCorey Minyard 	} else {
7359cdb2e7SAndy Shevchenko 		t = match_string(si_to_str, -1, si_type_str);
7459cdb2e7SAndy Shevchenko 		if (t < 0) {
7541b766d6SCorey Minyard 			pr_warn("Interface type specified for interface %d, was invalid: %s\n",
7641b766d6SCorey Minyard 				i, si_type_str);
7741b766d6SCorey Minyard 			return;
7841b766d6SCorey Minyard 		}
7959cdb2e7SAndy Shevchenko 		p.type = t;
8059cdb2e7SAndy Shevchenko 	}
8141b766d6SCorey Minyard 
823cd83bacSCorey Minyard 	p.regsize = regsizes[i];
83*242c6fd4SEmilio Perez 	p.regspacing = regspacings[i];
843cd83bacSCorey Minyard 	p.slave_addr = slave_addrs[i];
853cd83bacSCorey Minyard 	p.addr_source = SI_HARDCODED;
863cd83bacSCorey Minyard 	p.regshift = regshifts[i];
873cd83bacSCorey Minyard 	p.addr = addr;
883cd83bacSCorey Minyard 	p.space = addr_space;
8941b766d6SCorey Minyard 
901a84df2dSCorey Minyard 	ipmi_platform_add("hardcode-ipmi-si", i, &p);
9141b766d6SCorey Minyard }
9241b766d6SCorey Minyard 
ipmi_hardcode_init(void)9341b766d6SCorey Minyard void __init ipmi_hardcode_init(void)
9441b766d6SCorey Minyard {
9541b766d6SCorey Minyard 	unsigned int i;
967a453308SCorey Minyard 	char *str;
9741b766d6SCorey Minyard 	char *si_type[SI_MAX_PARMS];
987a453308SCorey Minyard 
99a885bcfdSTony Camuso 	memset(si_type, 0, sizeof(si_type));
100a885bcfdSTony Camuso 
1017a453308SCorey Minyard 	/* Parse out the si_type string into its components. */
1027a453308SCorey Minyard 	str = si_type_str;
1037a453308SCorey Minyard 	if (*str != '\0') {
1047a453308SCorey Minyard 		for (i = 0; (i < SI_MAX_PARMS) && (*str != '\0'); i++) {
1057a453308SCorey Minyard 			si_type[i] = str;
1067a453308SCorey Minyard 			str = strchr(str, ',');
1077a453308SCorey Minyard 			if (str) {
1087a453308SCorey Minyard 				*str = '\0';
1097a453308SCorey Minyard 				str++;
1107a453308SCorey Minyard 			} else {
1117a453308SCorey Minyard 				break;
1127a453308SCorey Minyard 			}
1137a453308SCorey Minyard 		}
1147a453308SCorey Minyard 	}
1157a453308SCorey Minyard 
1167a453308SCorey Minyard 	for (i = 0; i < SI_MAX_PARMS; i++) {
11741b766d6SCorey Minyard 		if (i < num_ports && ports[i])
11841b766d6SCorey Minyard 			ipmi_hardcode_init_one(si_type[i], i, ports[i],
1193cd83bacSCorey Minyard 					       IPMI_IO_ADDR_SPACE);
12041b766d6SCorey Minyard 		if (i < num_addrs && addrs[i])
12141b766d6SCorey Minyard 			ipmi_hardcode_init_one(si_type[i], i, addrs[i],
1223cd83bacSCorey Minyard 					       IPMI_MEM_ADDR_SPACE);
12341b766d6SCorey Minyard 	}
12441b766d6SCorey Minyard }
1257a453308SCorey Minyard 
1261a84df2dSCorey Minyard 
ipmi_si_hardcode_exit(void)12741b766d6SCorey Minyard void ipmi_si_hardcode_exit(void)
12841b766d6SCorey Minyard {
129e17c6571SCorey Minyard 	ipmi_remove_platform_device_by_name("hardcode-ipmi-si");
13041b766d6SCorey Minyard }
13141b766d6SCorey Minyard 
13241b766d6SCorey Minyard /*
13341b766d6SCorey Minyard  * Returns true of the given address exists as a hardcoded address,
13441b766d6SCorey Minyard  * false if not.
13541b766d6SCorey Minyard  */
ipmi_si_hardcode_match(int addr_space,unsigned long addr)136f6296bdcSCorey Minyard int ipmi_si_hardcode_match(int addr_space, unsigned long addr)
13741b766d6SCorey Minyard {
13841b766d6SCorey Minyard 	unsigned int i;
13941b766d6SCorey Minyard 
140f6296bdcSCorey Minyard 	if (addr_space == IPMI_IO_ADDR_SPACE) {
14141b766d6SCorey Minyard 		for (i = 0; i < num_ports; i++) {
14241b766d6SCorey Minyard 			if (ports[i] == addr)
14341b766d6SCorey Minyard 				return 1;
14441b766d6SCorey Minyard 		}
1457a453308SCorey Minyard 	} else {
14641b766d6SCorey Minyard 		for (i = 0; i < num_addrs; i++) {
14741b766d6SCorey Minyard 			if (addrs[i] == addr)
14841b766d6SCorey Minyard 				return 1;
14941b766d6SCorey Minyard 		}
1507a453308SCorey Minyard 	}
1517a453308SCorey Minyard 
15241b766d6SCorey Minyard 	return 0;
1537a453308SCorey Minyard }
154