brintos

brintos / linux-shallow public Read only

0
0
Text · 7.7 KiB · 231b379 Raw
326 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * IBM Real-Time Linux driver4 *5 * Copyright (C) IBM Corporation, 20106 *7 * Author: Keith Mannthey <kmannth@us.ibm.com>8 *         Vernon Mauery <vernux@us.ibm.com>9 */10 11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt12 13#include <linux/kernel.h>14#include <linux/delay.h>15#include <linux/module.h>16#include <linux/io.h>17#include <linux/dmi.h>18#include <linux/efi.h>19#include <linux/mutex.h>20#include <asm/bios_ebda.h>21 22#include <linux/io-64-nonatomic-lo-hi.h>23 24static bool force;25module_param(force, bool, 0);26MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");27 28static bool debug;29module_param(debug, bool, 0644);30MODULE_PARM_DESC(debug, "Show debug output");31 32MODULE_DESCRIPTION("IBM Premium Real Time Mode (PRTM) driver");33MODULE_LICENSE("GPL");34MODULE_AUTHOR("Keith Mannthey <kmmanth@us.ibm.com>");35MODULE_AUTHOR("Vernon Mauery <vernux@us.ibm.com>");36 37#define RTL_ADDR_TYPE_IO    138#define RTL_ADDR_TYPE_MMIO  239 40#define RTL_CMD_ENTER_PRTM  141#define RTL_CMD_EXIT_PRTM   242 43/* The RTL table as presented by the EBDA: */44struct ibm_rtl_table {45	char signature[5]; /* signature should be "_RTL_" */46	u8 version;47	u8 rt_status;48	u8 command;49	u8 command_status;50	u8 cmd_address_type;51	u8 cmd_granularity;52	u8 cmd_offset;53	u16 reserve1;54	u32 cmd_port_address; /* platform dependent address */55	u32 cmd_port_value;   /* platform dependent value */56} __attribute__((packed));57 58/* to locate "_RTL_" signature do a masked 5-byte integer compare */59#define RTL_SIGNATURE 0x0000005f4c54525fULL60#define RTL_MASK      0x000000ffffffffffULL61 62#define RTL_DEBUG(fmt, ...)				\63do {							\64	if (debug)					\65		pr_info(fmt, ##__VA_ARGS__);		\66} while (0)67 68static DEFINE_MUTEX(rtl_lock);69static struct ibm_rtl_table __iomem *rtl_table;70static void __iomem *ebda_map;71static void __iomem *rtl_cmd_addr;72static u8 rtl_cmd_type;73static u8 rtl_cmd_width;74 75static void __iomem *rtl_port_map(phys_addr_t addr, unsigned long len)76{77	if (rtl_cmd_type == RTL_ADDR_TYPE_MMIO)78		return ioremap(addr, len);79	return ioport_map(addr, len);80}81 82static void rtl_port_unmap(void __iomem *addr)83{84	if (addr && rtl_cmd_type == RTL_ADDR_TYPE_MMIO)85		iounmap(addr);86	else87		ioport_unmap(addr);88}89 90static int ibm_rtl_write(u8 value)91{92	int ret = 0, count = 0;93	u32 cmd_port_val;94 95	RTL_DEBUG("%s(%d)\n", __func__, value);96 97	value = value == 1 ? RTL_CMD_ENTER_PRTM : RTL_CMD_EXIT_PRTM;98 99	mutex_lock(&rtl_lock);100 101	if (ioread8(&rtl_table->rt_status) != value) {102		iowrite8(value, &rtl_table->command);103 104		switch (rtl_cmd_width) {105		case 8:106			cmd_port_val = ioread8(&rtl_table->cmd_port_value);107			RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);108			iowrite8((u8)cmd_port_val, rtl_cmd_addr);109			break;110		case 16:111			cmd_port_val = ioread16(&rtl_table->cmd_port_value);112			RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);113			iowrite16((u16)cmd_port_val, rtl_cmd_addr);114			break;115		case 32:116			cmd_port_val = ioread32(&rtl_table->cmd_port_value);117			RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val);118			iowrite32(cmd_port_val, rtl_cmd_addr);119			break;120		}121 122		while (ioread8(&rtl_table->command)) {123			msleep(10);124			if (count++ > 500) {125				pr_err("Hardware not responding to "126				       "mode switch request\n");127				ret = -EIO;128				break;129			}130 131		}132 133		if (ioread8(&rtl_table->command_status)) {134			RTL_DEBUG("command_status reports failed command\n");135			ret = -EIO;136		}137	}138 139	mutex_unlock(&rtl_lock);140	return ret;141}142 143static ssize_t rtl_show_version(struct device *dev,144                                struct device_attribute *attr,145                                char *buf)146{147	return sprintf(buf, "%d\n", (int)ioread8(&rtl_table->version));148}149 150static ssize_t rtl_show_state(struct device *dev,151                              struct device_attribute *attr,152                              char *buf)153{154	return sprintf(buf, "%d\n", ioread8(&rtl_table->rt_status));155}156 157static ssize_t rtl_set_state(struct device *dev,158                             struct device_attribute *attr,159                             const char *buf,160                             size_t count)161{162	ssize_t ret;163 164	if (count < 1 || count > 2)165		return -EINVAL;166 167	switch (buf[0]) {168	case '0':169		ret = ibm_rtl_write(0);170		break;171	case '1':172		ret = ibm_rtl_write(1);173		break;174	default:175		ret = -EINVAL;176	}177	if (ret >= 0)178		ret = count;179 180	return ret;181}182 183static const struct bus_type rtl_subsys = {184	.name = "ibm_rtl",185	.dev_name = "ibm_rtl",186};187 188static DEVICE_ATTR(version, S_IRUGO, rtl_show_version, NULL);189static DEVICE_ATTR(state, 0600, rtl_show_state, rtl_set_state);190 191static struct device_attribute *rtl_attributes[] = {192	&dev_attr_version,193	&dev_attr_state,194	NULL195};196 197 198static int rtl_setup_sysfs(void) {199	int ret, i;200 201	ret = subsys_system_register(&rtl_subsys, NULL);202	if (!ret) {203		struct device *dev_root = bus_get_dev_root(&rtl_subsys);204 205		if (dev_root) {206			for (i = 0; rtl_attributes[i]; i ++)207				device_create_file(dev_root, rtl_attributes[i]);208			put_device(dev_root);209		}210	}211	return ret;212}213 214static void rtl_teardown_sysfs(void) {215	struct device *dev_root = bus_get_dev_root(&rtl_subsys);216	int i;217 218	if (dev_root) {219		for (i = 0; rtl_attributes[i]; i ++)220			device_remove_file(dev_root, rtl_attributes[i]);221		put_device(dev_root);222	}223	bus_unregister(&rtl_subsys);224}225 226 227static const struct dmi_system_id ibm_rtl_dmi_table[] __initconst = {228	{                                                  \229		.matches = {                               \230			DMI_MATCH(DMI_SYS_VENDOR, "IBM"),  \231		},                                         \232	},233	{ }234};235 236static int __init ibm_rtl_init(void) {237	unsigned long ebda_addr, ebda_size;238	unsigned int ebda_kb;239	int ret = -ENODEV, i;240 241	if (force)242		pr_warn("module loaded by force\n");243	/* first ensure that we are running on IBM HW */244	else if (efi_enabled(EFI_BOOT) || !dmi_check_system(ibm_rtl_dmi_table))245		return -ENODEV;246 247	/* Get the address for the Extended BIOS Data Area */248	ebda_addr = get_bios_ebda();249	if (!ebda_addr) {250		RTL_DEBUG("no BIOS EBDA found\n");251		return -ENODEV;252	}253 254	ebda_map = ioremap(ebda_addr, 4);255	if (!ebda_map)256		return -ENOMEM;257 258	/* First word in the EDBA is the Size in KB */259	ebda_kb = ioread16(ebda_map);260	RTL_DEBUG("EBDA is %d kB\n", ebda_kb);261 262	if (ebda_kb == 0)263		goto out;264 265	iounmap(ebda_map);266	ebda_size = ebda_kb*1024;267 268	/* Remap the whole table */269	ebda_map = ioremap(ebda_addr, ebda_size);270	if (!ebda_map)271		return -ENOMEM;272 273	/* search for the _RTL_ signature at the start of the table */274	for (i = 0 ; i < ebda_size/sizeof(unsigned int); i++) {275		struct ibm_rtl_table __iomem * tmp;276		tmp = (struct ibm_rtl_table __iomem *) (ebda_map+i);277		if ((readq(&tmp->signature) & RTL_MASK) == RTL_SIGNATURE) {278			phys_addr_t addr;279			unsigned int plen;280			RTL_DEBUG("found RTL_SIGNATURE at %p\n", tmp);281			rtl_table = tmp;282			/* The address, value, width and offset are platform283			 * dependent and found in the ibm_rtl_table */284			rtl_cmd_width = ioread8(&rtl_table->cmd_granularity);285			rtl_cmd_type = ioread8(&rtl_table->cmd_address_type);286			RTL_DEBUG("rtl_cmd_width = %u, rtl_cmd_type = %u\n",287				  rtl_cmd_width, rtl_cmd_type);288			addr = ioread32(&rtl_table->cmd_port_address);289			RTL_DEBUG("addr = %#llx\n", (unsigned long long)addr);290			plen = rtl_cmd_width/sizeof(char);291			rtl_cmd_addr = rtl_port_map(addr, plen);292			RTL_DEBUG("rtl_cmd_addr = %p\n", rtl_cmd_addr);293			if (!rtl_cmd_addr) {294				ret = -ENOMEM;295				break;296			}297			ret = rtl_setup_sysfs();298			break;299		}300	}301 302out:303	if (ret) {304		iounmap(ebda_map);305		rtl_port_unmap(rtl_cmd_addr);306	}307 308	return ret;309}310 311static void __exit ibm_rtl_exit(void)312{313	if (rtl_table) {314		RTL_DEBUG("cleaning up");315		/* do not leave the machine in SMI-free mode */316		ibm_rtl_write(0);317		/* unmap, unlink and remove all traces */318		rtl_teardown_sysfs();319		iounmap(ebda_map);320		rtl_port_unmap(rtl_cmd_addr);321	}322}323 324module_init(ibm_rtl_init);325module_exit(ibm_rtl_exit);326