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