diff options
Diffstat (limited to 'drivers/char')
55 files changed, 1704 insertions, 1995 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index c28dca0c613d..e538061eadcb 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -66,34 +66,6 @@ config TTY_PRINTK If unsure, say N. -config BFIN_OTP - tristate "Blackfin On-Chip OTP Memory Support" - depends on BLACKFIN && (BF51x || BF52x || BF54x) - default y - help - If you say Y here, you will get support for a character device - interface into the One Time Programmable memory pages that are - stored on the Blackfin processor. This will not get you access - to the secure memory pages however. You will need to write your - own secure code and reader for that. - - To compile this driver as a module, choose M here: the module - will be called bfin-otp. - - If unsure, it is safe to say Y. - -config BFIN_OTP_WRITE_ENABLE - bool "Enable writing support of OTP pages" - depends on BFIN_OTP - default n - help - If you say Y here, you will enable support for writing of the - OTP pages. This is dangerous by nature as you can only program - the pages once, so only enable this option when you actually - need it so as to not inadvertently clobber data. - - If unsure, say N. - config PRINTER tristate "Parallel printer support" depends on PARPORT @@ -264,7 +236,7 @@ source "drivers/char/hw_random/Kconfig" config NVRAM tristate "/dev/nvram support" - depends on ATARI || X86 || (ARM && RTC_DRV_CMOS) || GENERIC_NVRAM + depends on ATARI || X86 || GENERIC_NVRAM ---help--- If you say Y here and create a character special file /dev/nvram with major number 10 and minor number 144 using mknod ("man mknod"), @@ -346,15 +318,6 @@ config EFI_RTC bool "EFI Real Time Clock Services" depends on IA64 -config DS1302 - tristate "DS1302 RTC support" - depends on M32R && (PLAT_M32700UT || PLAT_OPSPUT) - help - If you say Y here and create a character special file /dev/rtc with - major number 121 and minor number 0 using mknod ("man mknod"), you - will get access to the real time clock (or hardware clock) built - into your computer. - endif # RTC_LIB config DTLK @@ -575,17 +538,6 @@ config DEVPORT source "drivers/s390/char/Kconfig" -config TILE_SROM - tristate "Character-device access via hypervisor to the Tilera SPI ROM" - depends on TILE - default y - ---help--- - This device provides character-level read-write access - to the SROM, typically via the "0", "1", and "2" devices - in /dev/srom/. The Tilera hypervisor makes the flash - device appear much like a simple EEPROM, and knows - how to partition a single ROM for multiple purposes. - source "drivers/char/xillybus/Kconfig" endmenu diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 7dc3abe66464..c97c768cd1dd 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -14,7 +14,6 @@ obj-$(CONFIG_MSPEC) += mspec.o obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o obj-$(CONFIG_IBM_BSR) += bsr.o obj-$(CONFIG_SGI_MBCS) += mbcs.o -obj-$(CONFIG_BFIN_OTP) += bfin-otp.o obj-$(CONFIG_PRINTER) += lp.o @@ -26,7 +25,6 @@ obj-$(CONFIG_SONYPI) += sonypi.o obj-$(CONFIG_RTC) += rtc.o obj-$(CONFIG_HPET) += hpet.o obj-$(CONFIG_EFI_RTC) += efirtc.o -obj-$(CONFIG_DS1302) += ds1302.o obj-$(CONFIG_XILINX_HWICAP) += xilinx_hwicap/ ifeq ($(CONFIG_GENERIC_NVRAM),y) obj-$(CONFIG_NVRAM) += generic_nvram.o @@ -57,6 +55,5 @@ obj-$(CONFIG_PS3_FLASH) += ps3flash.o obj-$(CONFIG_JS_RTC) += js-rtc.o js-rtc-y = rtc.o -obj-$(CONFIG_TILE_SROM) += tile-srom.o obj-$(CONFIG_XILLYBUS) += xillybus/ obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o diff --git a/drivers/char/bfin-otp.c b/drivers/char/bfin-otp.c deleted file mode 100644 index 0584025bb0c2..000000000000 --- a/drivers/char/bfin-otp.c +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Blackfin On-Chip OTP Memory Interface - * - * Copyright 2007-2009 Analog Devices Inc. - * - * Enter bugs at http://blackfin.uclinux.org/ - * - * Licensed under the GPL-2 or later. - */ - -#include <linux/device.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/init.h> -#include <linux/miscdevice.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/types.h> -#include <mtd/mtd-abi.h> - -#include <asm/blackfin.h> -#include <asm/bfrom.h> -#include <linux/uaccess.h> - -#define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args) -#define stampit() stamp("here i am") -#define pr_init(fmt, args...) ({ static const __initconst char __fmt[] = fmt; printk(__fmt, ## args); }) - -#define DRIVER_NAME "bfin-otp" -#define PFX DRIVER_NAME ": " - -static DEFINE_MUTEX(bfin_otp_lock); - -/** - * bfin_otp_read - Read OTP pages - * - * All reads must be in half page chunks (half page == 64 bits). - */ -static ssize_t bfin_otp_read(struct file *file, char __user *buff, size_t count, loff_t *pos) -{ - ssize_t bytes_done; - u32 page, flags, ret; - u64 content; - - stampit(); - - if (count % sizeof(u64)) - return -EMSGSIZE; - - if (mutex_lock_interruptible(&bfin_otp_lock)) - return -ERESTARTSYS; - - bytes_done = 0; - page = *pos / (sizeof(u64) * 2); - while (bytes_done < count) { - flags = (*pos % (sizeof(u64) * 2) ? OTP_UPPER_HALF : OTP_LOWER_HALF); - stamp("processing page %i (0x%x:%s)", page, flags, - (flags & OTP_UPPER_HALF ? "upper" : "lower")); - ret = bfrom_OtpRead(page, flags, &content); - if (ret & OTP_MASTER_ERROR) { - stamp("error from otp: 0x%x", ret); - bytes_done = -EIO; - break; - } - if (copy_to_user(buff + bytes_done, &content, sizeof(content))) { - bytes_done = -EFAULT; - break; - } - if (flags & OTP_UPPER_HALF) - ++page; - bytes_done += sizeof(content); - *pos += sizeof(content); - } - - mutex_unlock(&bfin_otp_lock); - - return bytes_done; -} - -#ifdef CONFIG_BFIN_OTP_WRITE_ENABLE -static bool allow_writes; - -/** - * bfin_otp_init_timing - setup OTP timing parameters - * - * Required before doing any write operation. Algorithms from HRM. - */ -static u32 bfin_otp_init_timing(void) -{ - u32 tp1, tp2, tp3, timing; - - tp1 = get_sclk() / 1000000; - tp2 = (2 * get_sclk() / 10000000) << 8; - tp3 = (0x1401) << 15; - timing = tp1 | tp2 | tp3; - if (bfrom_OtpCommand(OTP_INIT, timing)) - return 0; - - return timing; -} - -/** - * bfin_otp_deinit_timing - set timings to only allow reads - * - * Should be called after all writes are done. - */ -static void bfin_otp_deinit_timing(u32 timing) -{ - /* mask bits [31:15] so that any attempts to write fail */ - bfrom_OtpCommand(OTP_CLOSE, 0); - bfrom_OtpCommand(OTP_INIT, timing & ~(-1 << 15)); - bfrom_OtpCommand(OTP_CLOSE, 0); -} - -/** - * bfin_otp_write - write OTP pages - * - * All writes must be in half page chunks (half page == 64 bits). - */ -static ssize_t bfin_otp_write(struct file *filp, const char __user *buff, size_t count, loff_t *pos) -{ - ssize_t bytes_done; - u32 timing, page, base_flags, flags, ret; - u64 content; - - if (!allow_writes) - return -EACCES; - - if (count % sizeof(u64)) - return -EMSGSIZE; - - if (mutex_lock_interruptible(&bfin_otp_lock)) - return -ERESTARTSYS; - - stampit(); - - timing = bfin_otp_init_timing(); - if (timing == 0) { - mutex_unlock(&bfin_otp_lock); - return -EIO; - } - - base_flags = OTP_CHECK_FOR_PREV_WRITE; - - bytes_done = 0; - page = *pos / (sizeof(u64) * 2); - while (bytes_done < count) { - flags = base_flags | (*pos % (sizeof(u64) * 2) ? OTP_UPPER_HALF : OTP_LOWER_HALF); - stamp("processing page %i (0x%x:%s) from %p", page, flags, - (flags & OTP_UPPER_HALF ? "upper" : "lower"), buff + bytes_done); - if (copy_from_user(&content, buff + bytes_done, sizeof(content))) { - bytes_done = -EFAULT; - break; - } - ret = bfrom_OtpWrite(page, flags, &content); - if (ret & OTP_MASTER_ERROR) { - stamp("error from otp: 0x%x", ret); - bytes_done = -EIO; - break; - } - if (flags & OTP_UPPER_HALF) - ++page; - bytes_done += sizeof(content); - *pos += sizeof(content); - } - - bfin_otp_deinit_timing(timing); - - mutex_unlock(&bfin_otp_lock); - - return bytes_done; -} - -static long bfin_otp_ioctl(struct file *filp, unsigned cmd, unsigned long arg) -{ - stampit(); - - switch (cmd) { - case OTPLOCK: { - u32 timing; - int ret = -EIO; - - if (!allow_writes) - return -EACCES; - - if (mutex_lock_interruptible(&bfin_otp_lock)) - return -ERESTARTSYS; - - timing = bfin_otp_init_timing(); - if (timing) { - u32 otp_result = bfrom_OtpWrite(arg, OTP_LOCK, NULL); - stamp("locking page %lu resulted in 0x%x", arg, otp_result); - if (!(otp_result & OTP_MASTER_ERROR)) - ret = 0; - - bfin_otp_deinit_timing(timing); - } - - mutex_unlock(&bfin_otp_lock); - - return ret; - } - - case MEMLOCK: - allow_writes = false; - return 0; - - case MEMUNLOCK: - allow_writes = true; - return 0; - } - - return -EINVAL; -} -#else -# define bfin_otp_write NULL -# define bfin_otp_ioctl NULL -#endif - -static const struct file_operations bfin_otp_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = bfin_otp_ioctl, - .read = bfin_otp_read, - .write = bfin_otp_write, - .llseek = default_llseek, -}; - -static struct miscdevice bfin_otp_misc_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = DRIVER_NAME, - .fops = &bfin_otp_fops, -}; -module_misc_device(bfin_otp_misc_device); - -MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>"); -MODULE_DESCRIPTION("Blackfin OTP Memory Interface"); -MODULE_LICENSE("GPL"); diff --git a/drivers/char/ds1302.c b/drivers/char/ds1302.c deleted file mode 100644 index 8e16ad5d6d89..000000000000 --- a/drivers/char/ds1302.c +++ /dev/null @@ -1,357 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/*!*************************************************************************** -*! -*! FILE NAME : ds1302.c -*! -*! DESCRIPTION: Implements an interface for the DS1302 RTC -*! -*! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init, get_rtc_status -*! -*! --------------------------------------------------------------------------- -*! -*! (C) Copyright 1999, 2000, 2001 Axis Communications AB, LUND, SWEDEN -*! -*!***************************************************************************/ - - -#include <linux/fs.h> -#include <linux/init.h> -#include <linux/mm.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/bcd.h> -#include <linux/mutex.h> -#include <linux/uaccess.h> -#include <linux/io.h> - -#include <asm/rtc.h> -#if defined(CONFIG_M32R) -#include <asm/m32r.h> -#endif - -#define RTC_MAJOR_NR 121 /* local major, change later */ - -static DEFINE_MUTEX(rtc_mutex); -static const char ds1302_name[] = "ds1302"; - -/* Send 8 bits. */ -static void -out_byte_rtc(unsigned int reg_addr, unsigned char x) -{ - //RST H - outw(0x0001,(unsigned long)PLD_RTCRSTODT); - //write data - outw(((x<<8)|(reg_addr&0xff)),(unsigned long)PLD_RTCWRDATA); - //WE - outw(0x0002,(unsigned long)PLD_RTCCR); - //wait - while(inw((unsigned long)PLD_RTCCR)); - - //RST L - outw(0x0000,(unsigned long)PLD_RTCRSTODT); - -} - -static unsigned char -in_byte_rtc(unsigned int reg_addr) -{ - unsigned char retval; - - //RST H - outw(0x0001,(unsigned long)PLD_RTCRSTODT); - //write data - outw((reg_addr&0xff),(unsigned long)PLD_RTCRDDATA); - //RE - outw(0x0001,(unsigned long)PLD_RTCCR); - //wait - while(inw((unsigned long)PLD_RTCCR)); - - //read data - retval=(inw((unsigned long)PLD_RTCRDDATA) & 0xff00)>>8; - - //RST L - outw(0x0000,(unsigned long)PLD_RTCRSTODT); - - return retval; -} - -/* Enable writing. */ - -static void -ds1302_wenable(void) -{ - out_byte_rtc(0x8e,0x00); -} - -/* Disable writing. */ - -static void -ds1302_wdisable(void) -{ - out_byte_rtc(0x8e,0x80); -} - - - -/* Read a byte from the selected register in the DS1302. */ - -unsigned char -ds1302_readreg(int reg) -{ - unsigned char x; - - x=in_byte_rtc((0x81 | (reg << 1))); /* read register */ - - return x; -} - -/* Write a byte to the selected register. */ - -void -ds1302_writereg(int reg, unsigned char val) -{ - ds1302_wenable(); - out_byte_rtc((0x80 | (reg << 1)),val); - ds1302_wdisable(); -} - -void -get_rtc_time(struct rtc_time *rtc_tm) -{ - unsigned long flags; - - local_irq_save(flags); - - rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS); - rtc_tm->tm_min = CMOS_READ(RTC_MINUTES); - rtc_tm->tm_hour = CMOS_READ(RTC_HOURS); - rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH); - rtc_tm->tm_mon = CMOS_READ(RTC_MONTH); - rtc_tm->tm_year = CMOS_READ(RTC_YEAR); - - local_irq_restore(flags); - - rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec); - rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min); - rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour); - rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday); - rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon); - rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year); - - /* - * Account for differences between how the RTC uses the values - * and how they are defined in a struct rtc_time; - */ - - if (rtc_tm->tm_year <= 69) - rtc_tm->tm_year += 100; - - rtc_tm->tm_mon--; -} - -static unsigned char days_in_mo[] = - {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - -/* ioctl that supports RTC_RD_TIME and RTC_SET_TIME (read and set time/date). */ - -static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - unsigned long flags; - - switch(cmd) { - case RTC_RD_TIME: /* read the time/date from RTC */ - { - struct rtc_time rtc_tm; - - memset(&rtc_tm, 0, sizeof (struct rtc_time)); - mutex_lock(&rtc_mutex); - get_rtc_time(&rtc_tm); - mutex_unlock(&rtc_mutex); - if (copy_to_user((struct rtc_time*)arg, &rtc_tm, sizeof(struct rtc_time))) - return -EFAULT; - return 0; - } - - case RTC_SET_TIME: /* set the RTC */ - { - struct rtc_time rtc_tm; - unsigned char mon, day, hrs, min, sec, leap_yr; - unsigned int yrs; - - if (!capable(CAP_SYS_TIME)) - return -EPERM; - - if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time))) - return -EFAULT; - - yrs = rtc_tm.tm_year + 1900; - mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ - day = rtc_tm.tm_mday; - hrs = rtc_tm.tm_hour; - min = rtc_tm.tm_min; - sec = rtc_tm.tm_sec; - - - if ((yrs < 1970) || (yrs > 2069)) - return -EINVAL; - - leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); - - if ((mon > 12) || (day == 0)) - return -EINVAL; - - if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) - return -EINVAL; - - if ((hrs >= 24) || (min >= 60) || (sec >= 60)) - return -EINVAL; - - if (yrs >= 2000) - yrs -= 2000; /* RTC (0, 1, ... 69) */ - else - yrs -= 1900; /* RTC (70, 71, ... 99) */ - - sec = bin2bcd(sec); - min = bin2bcd(min); - hrs = bin2bcd(hrs); - day = bin2bcd(day); - mon = bin2bcd(mon); - yrs = bin2bcd(yrs); - - mutex_lock(&rtc_mutex); - local_irq_save(flags); - CMOS_WRITE(yrs, RTC_YEAR); - CMOS_WRITE(mon, RTC_MONTH); - CMOS_WRITE(day, RTC_DAY_OF_MONTH); - CMOS_WRITE(hrs, RTC_HOURS); - CMOS_WRITE(min, RTC_MINUTES); - CMOS_WRITE(sec, RTC_SECONDS); - local_irq_restore(flags); - mutex_unlock(&rtc_mutex); - - /* Notice that at this point, the RTC is updated but - * the kernel is still running with the old time. - * You need to set that separately with settimeofday - * or adjtimex. - */ - return 0; - } - - case RTC_SET_CHARGE: /* set the RTC TRICKLE CHARGE register */ - { - int tcs_val; - - if (!capable(CAP_SYS_TIME)) - return -EPERM; - - if(copy_from_user(&tcs_val, (int*)arg, sizeof(int))) - return -EFAULT; - - mutex_lock(&rtc_mutex); - tcs_val = RTC_TCR_PATTERN | (tcs_val & 0x0F); - ds1302_writereg(RTC_TRICKLECHARGER, tcs_val); - mutex_unlock(&rtc_mutex); - return 0; - } - default: - return -EINVAL; - } -} - -int -get_rtc_status(char *buf) -{ - char *p; - struct rtc_time tm; - - p = buf; - - get_rtc_time(&tm); - - /* - * There is no way to tell if the luser has the RTC set for local - * time or for Universal Standard Time (GMT). Probably local though. - */ - - p += sprintf(p, - "rtc_time\t: %02d:%02d:%02d\n" - "rtc_date\t: %04d-%02d-%02d\n", - tm.tm_hour, tm.tm_min, tm.tm_sec, - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); - - return p - buf; -} - - -/* The various file operations we support. */ - -static const struct file_operations rtc_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = rtc_ioctl, - .llseek = noop_llseek, -}; - -/* Probe for the chip by writing something to its RAM and try reading it back. */ - -#define MAGIC_PATTERN 0x42 - -static int __init -ds1302_probe(void) -{ - int retval, res, baur; - - baur=(boot_cpu_data.bus_clock/(2*1000*1000)); - - printk("%s: Set PLD_RTCBAUR = %d\n", ds1302_name,baur); - - outw(0x0000,(unsigned long)PLD_RTCCR); - outw(0x0000,(unsigned long)PLD_RTCRSTODT); - outw(baur,(unsigned long)PLD_RTCBAUR); - - /* Try to talk to timekeeper. */ - - ds1302_wenable(); - /* write RAM byte 0 */ - /* write something magic */ - out_byte_rtc(0xc0,MAGIC_PATTERN); - - /* read RAM byte 0 */ - if((res = in_byte_rtc(0xc1)) == MAGIC_PATTERN) { - char buf[100]; - ds1302_wdisable(); - printk("%s: RTC found.\n", ds1302_name); - get_rtc_status(buf); - printk(buf); - retval = 1; - } else { - printk("%s: RTC not found.\n", ds1302_name); - retval = 0; - } - - return retval; -} - - -/* Just probe for the RTC and register the device to handle the ioctl needed. */ - -int __init -ds1302_init(void) -{ - if (!ds1302_probe()) { - return -1; - } - return 0; -} - -static int __init ds1302_register(void) -{ - ds1302_init(); - if (register_chrdev(RTC_MAJOR_NR, ds1302_name, &rtc_fops)) { - printk(KERN_INFO "%s: unable to get major %d for rtc\n", - ds1302_name, RTC_MAJOR_NR); - return -1; - } - return 0; -} - -module_init(ds1302_register); diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 4d0f571c15f9..d53541e96bee 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -452,3 +452,10 @@ config UML_RANDOM (check your distro, or download from http://sourceforge.net/projects/gkernel/). rngd periodically reads /dev/hwrng and injects the entropy into /dev/random. + +config HW_RANDOM_KEYSTONE + depends on ARCH_KEYSTONE + default HW_RANDOM + tristate "TI Keystone NETCP SA Hardware random number generator" + help + This option enables Keystone's hardware random generator. diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index b780370bd4eb..533e913c93d1 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -38,3 +38,4 @@ obj-$(CONFIG_HW_RANDOM_MESON) += meson-rng.o obj-$(CONFIG_HW_RANDOM_CAVIUM) += cavium-rng.o cavium-rng-vf.o obj-$(CONFIG_HW_RANDOM_MTK) += mtk-rng.o obj-$(CONFIG_HW_RANDOM_S390) += s390-trng.o +obj-$(CONFIG_HW_RANDOM_KEYSTONE) += ks-sa-rng.o diff --git a/drivers/char/hw_random/bcm2835-rng.c b/drivers/char/hw_random/bcm2835-rng.c index 7a84cec30c3a..6767d965c36c 100644 --- a/drivers/char/hw_random/bcm2835-rng.c +++ b/drivers/char/hw_random/bcm2835-rng.c @@ -163,6 +163,8 @@ static int bcm2835_rng_probe(struct platform_device *pdev) /* Clock is optional on most platforms */ priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk) && PTR_ERR(priv->clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; priv->rng.name = pdev->name; priv->rng.init = bcm2835_rng_init; diff --git a/drivers/char/hw_random/cavium-rng-vf.c b/drivers/char/hw_random/cavium-rng-vf.c index dd1007aecb10..2d1352b67168 100644 --- a/drivers/char/hw_random/cavium-rng-vf.c +++ b/drivers/char/hw_random/cavium-rng-vf.c @@ -77,7 +77,7 @@ static int cavium_rng_probe_vf(struct pci_dev *pdev, } /* Remove the VF */ -void cavium_rng_remove_vf(struct pci_dev *pdev) +static void cavium_rng_remove_vf(struct pci_dev *pdev) { struct cavium_rng *rng; diff --git a/drivers/char/hw_random/cavium-rng.c b/drivers/char/hw_random/cavium-rng.c index a944e0a47f42..63d6e68c24d2 100644 --- a/drivers/char/hw_random/cavium-rng.c +++ b/drivers/char/hw_random/cavium-rng.c @@ -62,7 +62,7 @@ static int cavium_rng_probe(struct pci_dev *pdev, } /* Disable VF and RNG Hardware */ -void cavium_rng_remove(struct pci_dev *pdev) +static void cavium_rng_remove(struct pci_dev *pdev) { struct cavium_rng_pf *rng; diff --git a/drivers/char/hw_random/imx-rngc.c b/drivers/char/hw_random/imx-rngc.c index eca87249bcff..250123bc4905 100644 --- a/drivers/char/hw_random/imx-rngc.c +++ b/drivers/char/hw_random/imx-rngc.c @@ -300,7 +300,7 @@ static int __maybe_unused imx_rngc_resume(struct device *dev) return 0; } -SIMPLE_DEV_PM_OPS(imx_rngc_pm_ops, imx_rngc_suspend, imx_rngc_resume); +static SIMPLE_DEV_PM_OPS(imx_rngc_pm_ops, imx_rngc_suspend, imx_rngc_resume); static const struct of_device_id imx_rngc_dt_ids[] = { { .compatible = "fsl,imx25-rngb", .data = NULL, }, diff --git a/drivers/char/hw_random/ks-sa-rng.c b/drivers/char/hw_random/ks-sa-rng.c new file mode 100644 index 000000000000..62c6696c1dbd --- /dev/null +++ b/drivers/char/hw_random/ks-sa-rng.c @@ -0,0 +1,257 @@ +/* + * Random Number Generator driver for the Keystone SOC + * + * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Sandeep Nair + * Vitaly Andrianov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/hw_random.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/pm_runtime.h> +#include <linux/err.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/delay.h> + +#define SA_CMD_STATUS_OFS 0x8 + +/* TRNG enable control in SA System module*/ +#define SA_CMD_STATUS_REG_TRNG_ENABLE BIT(3) + +/* TRNG start control in TRNG module */ +#define TRNG_CNTL_REG_TRNG_ENABLE BIT(10) + +/* Data ready indicator in STATUS register */ +#define TRNG_STATUS_REG_READY BIT(0) + +/* Data ready clear control in INTACK register */ +#define TRNG_INTACK_REG_READY BIT(0) + +/* + * Number of samples taken to gather entropy during startup. + * If value is 0, the number of samples is 2^24 else + * equals value times 2^8. + */ +#define TRNG_DEF_STARTUP_CYCLES 0 +#define TRNG_CNTL_REG_STARTUP_CYCLES_SHIFT 16 + +/* + * Minimum number of samples taken to regenerate entropy + * If value is 0, the number of samples is 2^24 else + * equals value times 2^6. + */ +#define TRNG_DEF_MIN_REFILL_CYCLES 1 +#define TRNG_CFG_REG_MIN_REFILL_CYCLES_SHIFT 0 + +/* + * Maximum number of samples taken to regenerate entropy + * If value is 0, the number of samples is 2^24 else + * equals value times 2^8. + */ +#define TRNG_DEF_MAX_REFILL_CYCLES 0 +#define TRNG_CFG_REG_MAX_REFILL_CYCLES_SHIFT 16 + +/* Number of CLK input cycles between samples */ +#define TRNG_DEF_CLK_DIV_CYCLES 0 +#define TRNG_CFG_REG_SAMPLE_DIV_SHIFT 8 + +/* Maximum retries to get rng data */ +#define SA_MAX_RNG_DATA_RETRIES 5 +/* Delay between retries (in usecs) */ +#define SA_RNG_DATA_RETRY_DELAY 5 + +struct trng_regs { + u32 output_l; + u32 output_h; + u32 status; + u32 intmask; + u32 intack; + u32 control; + u32 config; +}; + +struct ks_sa_rng { + struct device *dev; + struct hwrng rng; + struct clk *clk; + struct regmap *regmap_cfg; + struct trng_regs *reg_rng; +}; + +static int ks_sa_rng_init(struct hwrng *rng) +{ + u32 value; + struct device *dev = (struct device *)rng->priv; + struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); + + /* Enable RNG module */ + regmap_write_bits(ks_sa_rng->regmap_cfg, SA_CMD_STATUS_OFS, + SA_CMD_STATUS_REG_TRNG_ENABLE, + SA_CMD_STATUS_REG_TRNG_ENABLE); + + /* Configure RNG module */ + writel(0, &ks_sa_rng->reg_rng->control); + value = TRNG_DEF_STARTUP_CYCLES << TRNG_CNTL_REG_STARTUP_CYCLES_SHIFT; + writel(value, &ks_sa_rng->reg_rng->control); + + value = (TRNG_DEF_MIN_REFILL_CYCLES << + TRNG_CFG_REG_MIN_REFILL_CYCLES_SHIFT) | + (TRNG_DEF_MAX_REFILL_CYCLES << + TRNG_CFG_REG_MAX_REFILL_CYCLES_SHIFT) | + (TRNG_DEF_CLK_DIV_CYCLES << + TRNG_CFG_REG_SAMPLE_DIV_SHIFT); + + writel(value, &ks_sa_rng->reg_rng->config); + + /* Disable all interrupts from TRNG */ + writel(0, &ks_sa_rng->reg_rng->intmask); + + /* Enable RNG */ + value = readl(&ks_sa_rng->reg_rng->control); + value |= TRNG_CNTL_REG_TRNG_ENABLE; + writel(value, &ks_sa_rng->reg_rng->control); + + return 0; +} + +static void ks_sa_rng_cleanup(struct hwrng *rng) +{ + struct device *dev = (struct device *)rng->priv; + struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); + + /* Disable RNG */ + writel(0, &ks_sa_rng->reg_rng->control); + regmap_write_bits(ks_sa_rng->regmap_cfg, SA_CMD_STATUS_OFS, + SA_CMD_STATUS_REG_TRNG_ENABLE, 0); +} + +static int ks_sa_rng_data_read(struct hwrng *rng, u32 *data) +{ + struct device *dev = (struct device *)rng->priv; + struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); + + /* Read random data */ + data[0] = readl(&ks_sa_rng->reg_rng->output_l); + data[1] = readl(&ks_sa_rng->reg_rng->output_h); + + writel(TRNG_INTACK_REG_READY, &ks_sa_rng->reg_rng->intack); + + return sizeof(u32) * 2; +} + +static int ks_sa_rng_data_present(struct hwrng *rng, int wait) +{ + struct device *dev = (struct device *)rng->priv; + struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); + + u32 ready; + int j; + + for (j = 0; j < SA_MAX_RNG_DATA_RETRIES; j++) { + ready = readl(&ks_sa_rng->reg_rng->status); + ready &= TRNG_STATUS_REG_READY; + + if (ready || !wait) + break; + + udelay(SA_RNG_DATA_RETRY_DELAY); + } + + return ready; +} + +static int ks_sa_rng_probe(struct platform_device *pdev) +{ + struct ks_sa_rng *ks_sa_rng; + struct device *dev = &pdev->dev; + int ret; + struct resource *mem; + + ks_sa_rng = devm_kzalloc(dev, sizeof(*ks_sa_rng), GFP_KERNEL); + if (!ks_sa_rng) + return -ENOMEM; + + ks_sa_rng->dev = dev; + ks_sa_rng->rng = (struct hwrng) { + .name = "ks_sa_hwrng", + .init = ks_sa_rng_init, + .data_read = ks_sa_rng_data_read, + .data_present = ks_sa_rng_data_present, + .cleanup = ks_sa_rng_cleanup, + }; + ks_sa_rng->rng.priv = (unsigned long)dev; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ks_sa_rng->reg_rng = devm_ioremap_resource(dev, mem); + if (IS_ERR(ks_sa_rng->reg_rng)) + return PTR_ERR(ks_sa_rng->reg_rng); + + ks_sa_rng->regmap_cfg = + syscon_regmap_lookup_by_phandle(dev->of_node, + "ti,syscon-sa-cfg"); + + if (IS_ERR(ks_sa_rng->regmap_cfg)) { + dev_err(dev, "syscon_node_to_regmap failed\n"); + return -EINVAL; + } + + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "Failed to enable SA power-domain\n"); + pm_runtime_disable(dev); + return ret; + } + + platform_set_drvdata(pdev, ks_sa_rng); + + return devm_hwrng_register(&pdev->dev, &ks_sa_rng->rng); +} + +static int ks_sa_rng_remove(struct platform_device *pdev) +{ + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id ks_sa_rng_dt_match[] = { + { + .compatible = "ti,keystone-rng", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, ks_sa_rng_dt_match); + +static struct platform_driver ks_sa_rng_driver = { + .driver = { + .name = "ks-sa-rng", + .of_match_table = ks_sa_rng_dt_match, + }, + .probe = ks_sa_rng_probe, + .remove = ks_sa_rng_remove, +}; + +module_platform_driver(ks_sa_rng_driver); + +MODULE_DESCRIPTION("Keystone NETCP SA H/W Random Number Generator driver"); +MODULE_AUTHOR("Vitaly Andrianov <vitalya@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/mxc-rnga.c b/drivers/char/hw_random/mxc-rnga.c index 467362262651..f83bee513d91 100644 --- a/drivers/char/hw_random/mxc-rnga.c +++ b/drivers/char/hw_random/mxc-rnga.c @@ -16,16 +16,13 @@ * This driver is based on other RNG drivers. */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/kernel.h> #include <linux/clk.h> -#include <linux/err.h> -#include <linux/ioport.h> -#include <linux/platform_device.h> -#include <linux/hw_random.h> #include <linux/delay.h> +#include <linux/hw_random.h> #include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> /* RNGA Registers */ #define RNGA_CONTROL 0x00 @@ -197,10 +194,18 @@ static int __exit mxc_rnga_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id mxc_rnga_of_match[] = { + { .compatible = "fsl,imx21-rnga", }, + { .compatible = "fsl,imx31-rnga", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, mxc_rnga_of_match); + static struct platform_driver mxc_rnga_driver = { .driver = { - .name = "mxc_rnga", - }, + .name = "mxc_rnga", + .of_match_table = mxc_rnga_of_match, + }, .remove = __exit_p(mxc_rnga_remove), }; diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index 74d11ae6abe9..b65ff6962899 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -150,6 +150,7 @@ struct omap_rng_dev { const struct omap_rng_pdata *pdata; struct hwrng rng; struct clk *clk; + struct clk *clk_reg; }; static inline u32 omap_rng_read(struct omap_rng_dev *priv, u16 reg) @@ -480,6 +481,19 @@ static int omap_rng_probe(struct platform_device *pdev) } } + priv->clk_reg = devm_clk_get(&pdev->dev, "reg"); + if (IS_ERR(priv->clk_reg) && PTR_ERR(priv->clk_reg) == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (!IS_ERR(priv->clk_reg)) { + ret = clk_prepare_enable(priv->clk_reg); + if (ret) { + dev_err(&pdev->dev, + "Unable to enable the register clk: %d\n", + ret); + goto err_register; + } + } + ret = (dev->of_node) ? of_get_omap_rng_device_details(priv, pdev) : get_omap_rng_device_details(priv); if (ret) @@ -499,8 +513,8 @@ err_register: pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - if (!IS_ERR(priv->clk)) - clk_disable_unprepare(priv->clk); + clk_disable_unprepare(priv->clk_reg); + clk_disable_unprepare(priv->clk); err_ioremap: dev_err(dev, "initialization failed.\n"); return ret; @@ -517,8 +531,8 @@ static int omap_rng_remove(struct platform_device *pdev) pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - if (!IS_ERR(priv->clk)) - clk_disable_unprepare(priv->clk); + clk_disable_unprepare(priv->clk); + clk_disable_unprepare(priv->clk_reg); return 0; } diff --git a/drivers/char/hw_random/stm32-rng.c b/drivers/char/hw_random/stm32-rng.c index 63d84e6f1891..0d2328da3b76 100644 --- a/drivers/char/hw_random/stm32-rng.c +++ b/drivers/char/hw_random/stm32-rng.c @@ -16,15 +16,18 @@ #include <linux/delay.h> #include <linux/hw_random.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/pm_runtime.h> +#include <linux/reset.h> #include <linux/slab.h> #define RNG_CR 0x00 #define RNG_CR_RNGEN BIT(2) +#define RNG_CR_CED BIT(5) #define RNG_SR 0x04 #define RNG_SR_SEIS BIT(6) @@ -33,19 +36,12 @@ #define RNG_DR 0x08 -/* - * It takes 40 cycles @ 48MHz to generate each random number (e.g. <1us). - * At the time of writing STM32 parts max out at ~200MHz meaning a timeout - * of 500 leaves us a very comfortable margin for error. The loop to which - * the timeout applies takes at least 4 instructions per iteration so the - * timeout is enough to take us up to multi-GHz parts! - */ -#define RNG_TIMEOUT 500 - struct stm32_rng_private { struct hwrng rng; void __iomem *base; struct clk *clk; + struct reset_control *rst; + bool ced; }; static int stm32_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) @@ -59,13 +55,16 @@ static int stm32_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) while (max > sizeof(u32)) { sr = readl_relaxed(priv->base + RNG_SR); + /* Manage timeout which is based on timer and take */ + /* care of initial delay time when enabling rng */ if (!sr && wait) { - unsigned int timeout = RNG_TIMEOUT; - - do { - cpu_relax(); - sr = readl_relaxed(priv->base + RNG_SR); - } while (!sr && --timeout); + retval = readl_relaxed_poll_timeout_atomic(priv->base + + RNG_SR, + sr, sr, + 10, 50000); + if (retval) + dev_err((struct device *)priv->rng.priv, + "%s: timeout %x!\n", __func__, sr); } /* If error detected or data not ready... */ @@ -99,7 +98,11 @@ static int stm32_rng_init(struct hwrng *rng) if (err) return err; - writel_relaxed(RNG_CR_RNGEN, priv->base + RNG_CR); + if (priv->ced) + writel_relaxed(RNG_CR_RNGEN, priv->base + RNG_CR); + else + writel_relaxed(RNG_CR_RNGEN | RNG_CR_CED, + priv->base + RNG_CR); /* clear error indicators */ writel_relaxed(0, priv->base + RNG_SR); @@ -140,6 +143,15 @@ static int stm32_rng_probe(struct platform_device *ofdev) if (IS_ERR(priv->clk)) return PTR_ERR(priv->clk); + priv->rst = devm_reset_control_get(&ofdev->dev, NULL); + if (!IS_ERR(priv->rst)) { + reset_control_assert(priv->rst); + udelay(2); + reset_control_deassert(priv->rst); + } + + priv->ced = of_property_read_bool(np, "clock-error-detect"); + dev_set_drvdata(dev, priv); priv->rng.name = dev_driver_string(dev), diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index 3544abc0f9f9..3bda116c8aa0 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -96,6 +96,21 @@ config IPMI_POWEROFF endif # IPMI_HANDLER +config IPMI_KCS_BMC + tristate + +config ASPEED_KCS_IPMI_BMC + depends on ARCH_ASPEED || COMPILE_TEST + select IPMI_KCS_BMC + select REGMAP_MMIO + tristate "Aspeed KCS IPMI BMC driver" + help + Provides a driver for the KCS (Keyboard Controller Style) IPMI + interface found on Aspeed SOCs (AST2400 and AST2500). + + The driver implements the BMC side of the KCS contorller, it + provides the access of KCS IO space for BMC side. + config ASPEED_BT_IPMI_BMC depends on ARCH_ASPEED || COMPILE_TEST depends on REGMAP && REGMAP_MMIO && MFD_SYSCON diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index 33b899fcf14a..21e9e872d973 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -21,4 +21,6 @@ obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o +obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o +obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c index c95b93b7598b..40b9927c072c 100644 --- a/drivers/char/ipmi/bt-bmc.c +++ b/drivers/char/ipmi/bt-bmc.c @@ -1,10 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2015-2016, IBM Corporation. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. */ #include <linux/atomic.h> diff --git a/drivers/char/ipmi/ipmi_bt_sm.c b/drivers/char/ipmi/ipmi_bt_sm.c index feafdab734ae..fd4ea8d87d4b 100644 --- a/drivers/char/ipmi/ipmi_bt_sm.c +++ b/drivers/char/ipmi/ipmi_bt_sm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_bt_sm.c * @@ -5,26 +6,7 @@ * of the driver architecture at http://sourceforge.net/projects/openipmi * * Author: Rocky Craig <first.last@hp.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ + */ #include <linux/kernel.h> /* For printk. */ #include <linux/string.h> diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 5f1bc9174735..8ecfd47806fa 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_devintf.c * @@ -8,27 +9,6 @@ * source@mvista.com * * Copyright 2002 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/module.h> diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c index c5112b17d7ea..e2c143861b1e 100644 --- a/drivers/char/ipmi/ipmi_dmi.c +++ b/drivers/char/ipmi/ipmi_dmi.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0+ /* * A hack to create a platform device from a DMI entry. This will * allow autoloading of the IPMI drive based on SMBIOS entries. @@ -29,15 +29,6 @@ static struct ipmi_dmi_info *ipmi_dmi_infos; static int ipmi_dmi_nr __initdata; -#define set_prop_entry(_p_, _name_, type, val) \ -do { \ - struct property_entry *_p = &_p_; \ - _p->name = _name_; \ - _p->length = sizeof(type); \ - _p->is_string = false; \ - _p->value.type##_data = val; \ -} while(0) - static void __init dmi_add_platform_ipmi(unsigned long base_addr, u32 flags, u8 slave_addr, @@ -85,9 +76,10 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr, } if (si_type != SI_TYPE_INVALID) - set_prop_entry(p[pidx++], "ipmi-type", u8, si_type); - set_prop_entry(p[pidx++], "slave-addr", u8, slave_addr); - set_prop_entry(p[pidx++], "addr-source", u8, SI_SMBIOS); + p[pidx++] = PROPERTY_ENTRY_U8("ipmi-type", si_type); + + p[pidx++] = PROPERTY_ENTRY_U8("slave-addr", slave_addr); + p[pidx++] = PROPERTY_ENTRY_U8("addr-source", SI_SMBIOS); info = kmalloc(sizeof(*info), GFP_KERNEL); if (!info) { @@ -112,7 +104,7 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr, goto err; if (type == IPMI_DMI_TYPE_SSIF) { - set_prop_entry(p[pidx++], "i2c-addr", u16, base_addr); + p[pidx++] = PROPERTY_ENTRY_U16("i2c-addr", base_addr); goto add_properties; } diff --git a/drivers/char/ipmi/ipmi_dmi.h b/drivers/char/ipmi/ipmi_dmi.h index 6c21018e3668..8d2b094db8e6 100644 --- a/drivers/char/ipmi/ipmi_dmi.h +++ b/drivers/char/ipmi/ipmi_dmi.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * DMI defines for use by IPMI */ diff --git a/drivers/char/ipmi/ipmi_kcs_sm.c b/drivers/char/ipmi/ipmi_kcs_sm.c index 1da61af7f576..f4ea9f47230a 100644 --- a/drivers/char/ipmi/ipmi_kcs_sm.c +++ b/drivers/char/ipmi/ipmi_kcs_sm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_kcs_sm.c * @@ -8,27 +9,6 @@ * source@mvista.com * * Copyright 2002 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ /* diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index e0b0d7e2d976..361148938801 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_msghandler.c * @@ -8,27 +9,6 @@ * source@mvista.com * * Copyright 2002 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/module.h> diff --git a/drivers/char/ipmi/ipmi_powernv.c b/drivers/char/ipmi/ipmi_powernv.c index bcf493d8e238..e96500372ce2 100644 --- a/drivers/char/ipmi/ipmi_powernv.c +++ b/drivers/char/ipmi/ipmi_powernv.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * PowerNV OPAL IPMI driver * * Copyright 2014 IBM Corp. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. */ #define pr_fmt(fmt) "ipmi-powernv: " fmt diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index 38e6af1c8e38..7996337852f2 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_poweroff.c * @@ -9,27 +10,6 @@ * source@mvista.com * * Copyright 2002,2004 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/module.h> #include <linux/moduleparam.h> @@ -457,6 +437,24 @@ static int ipmi_dell_chassis_detect(ipmi_user_t user) } /* + * ipmi_hp_chassis_detect() + * HP PA-RISC servers rp3410/rp3440, the C8000 workstation and the rx2600 and + * zx6000 machines support IPMI vers 1 and don't set the chassis capability bit + * but they can handle a chassis poweroff or powercycle command. + */ + +#define HP_IANA_MFR_ID 0x0b +#define HP_BMC_PROD_ID 0x8201 +static int ipmi_hp_chassis_detect(ipmi_user_t user) +{ + if (mfg_id == HP_IANA_MFR_ID + && prod_id == HP_BMC_PROD_ID + && ipmi_version == 1) + return 1; + return 0; +} + +/* * Standard chassis support */ @@ -533,14 +531,16 @@ static struct poweroff_function poweroff_functions[] = { { .platform_type = "chassis", .detect = ipmi_dell_chassis_detect, .poweroff_func = ipmi_poweroff_chassis }, + { .platform_type = "chassis", + .detect = ipmi_hp_chassis_detect, + .poweroff_func = ipmi_poweroff_chassis }, /* Chassis should generally be last, other things should override it. */ { .platform_type = "chassis", .detect = ipmi_chassis_detect, .poweroff_func = ipmi_poweroff_chassis }, }; -#define NUM_PO_FUNCS (sizeof(poweroff_functions) \ - / sizeof(struct poweroff_function)) +#define NUM_PO_FUNCS ARRAY_SIZE(poweroff_functions) /* Called on a powerdown request. */ diff --git a/drivers/char/ipmi/ipmi_si.h b/drivers/char/ipmi/ipmi_si.h index 17ce5f7b89ab..52f6152d1fcb 100644 --- a/drivers/char/ipmi/ipmi_si.h +++ b/drivers/char/ipmi/ipmi_si.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * ipmi_si.h * diff --git a/drivers/char/ipmi/ipmi_si_hardcode.c b/drivers/char/ipmi/ipmi_si_hardcode.c index fa9a4780de36..10219f24546b 100644 --- a/drivers/char/ipmi/ipmi_si_hardcode.c +++ b/drivers/char/ipmi/ipmi_si_hardcode.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ #include <linux/moduleparam.h> #include "ipmi_si.h" diff --git a/drivers/char/ipmi/ipmi_si_hotmod.c b/drivers/char/ipmi/ipmi_si_hotmod.c index fc03b9be2f3d..a98ca42a50b1 100644 --- a/drivers/char/ipmi/ipmi_si_hotmod.c +++ b/drivers/char/ipmi/ipmi_si_hotmod.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_si_hotmod.c * diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 6768cb2dd740..ff870aa91cfe 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_si.c * @@ -10,27 +11,6 @@ * * Copyright 2002 MontaVista Software Inc. * Copyright 2006 IBM Corp., Christian Krafft <krafft@de.ibm.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ /* @@ -252,6 +232,12 @@ struct smi_info { /* Default driver model device. */ struct platform_device *pdev; + /* Have we added the device group to the device? */ + bool dev_group_added; + + /* Have we added the platform device? */ + bool pdev_registered; + /* Counters and things for the proc filesystem. */ atomic_t stats[SI_NUM_STATS]; @@ -275,7 +261,8 @@ static int num_max_busy_us; static bool unload_when_empty = true; static int try_smi_init(struct smi_info *smi); -static void cleanup_one_si(struct smi_info *to_clean); +static void shutdown_one_si(struct smi_info *smi_info); +static void cleanup_one_si(struct smi_info *smi_info); static void cleanup_ipmi_si(void); #ifdef DEBUG_TIMING @@ -2017,18 +2004,13 @@ int ipmi_si_add_smi(struct si_sm_io *io) ipmi_addr_src_to_str(new_smi->io.addr_source), si_to_str[new_smi->io.si_type]); - /* So we know not to free it unless we have allocated one. */ - new_smi->intf = NULL; - new_smi->si_sm = NULL; - new_smi->handlers = NULL; - list_add_tail(&new_smi->link, &smi_infos); if (initialized) { rv = try_smi_init(new_smi); if (rv) { - mutex_unlock(&smi_infos_lock); cleanup_one_si(new_smi); + mutex_unlock(&smi_infos_lock); return rv; } } @@ -2047,7 +2029,6 @@ static int try_smi_init(struct smi_info *new_smi) int rv = 0; int i; char *init_name = NULL; - bool platform_device_registered = false; pr_info(PFX "Trying %s-specified %s state machine at %s address 0x%lx, slave address 0x%x, irq %d\n", ipmi_addr_src_to_str(new_smi->io.addr_source), @@ -2090,6 +2071,7 @@ static int try_smi_init(struct smi_info *new_smi) new_smi->intf_num); if (!new_smi->pdev) { pr_err(PFX "Unable to allocate platform device\n"); + rv = -ENOMEM; goto out_err; } new_smi->io.dev = &new_smi->pdev->dev; @@ -2168,7 +2150,7 @@ static int try_smi_init(struct smi_info *new_smi) atomic_set(&new_smi->req_events, 1); } - if (new_smi->pdev) { + if (new_smi->pdev && !new_smi->pdev_registered) { rv = platform_device_add(new_smi->pdev); if (rv) { dev_err(new_smi->io.dev, @@ -2176,7 +2158,7 @@ static int try_smi_init(struct smi_info *new_smi) rv); goto out_err; } - platform_device_registered = true; + new_smi->pdev_registered = true; } dev_set_drvdata(new_smi->io.dev, new_smi); @@ -2185,8 +2167,9 @@ static int try_smi_init(struct smi_info *new_smi) dev_err(new_smi->io.dev, "Unable to add device attributes: error %d\n", rv); - goto out_err_stop_timer; + goto out_err; } + new_smi->dev_group_added = true; rv = ipmi_register_smi(&handlers, new_smi, @@ -2196,7 +2179,7 @@ static int try_smi_init(struct smi_info *new_smi) dev_err(new_smi->io.dev, "Unable to register device: error %d\n", rv); - goto out_err_remove_attrs; + goto out_err; } #ifdef CONFIG_IPMI_PROC_INTERFACE @@ -2206,7 +2189,7 @@ static int try_smi_init(struct smi_info *new_smi) if (rv) { dev_err(new_smi->io.dev, "Unable to create proc entry: %d\n", rv); - goto out_err_stop_timer; + goto out_err; } rv = ipmi_smi_add_proc_entry(new_smi->intf, "si_stats", @@ -2215,7 +2198,7 @@ static int try_smi_init(struct smi_info *new_smi) if (rv) { dev_err(new_smi->io.dev, "Unable to create proc entry: %d\n", rv); - goto out_err_stop_timer; + goto out_err; } rv = ipmi_smi_add_proc_entry(new_smi->intf, "params", @@ -2224,7 +2207,7 @@ static int try_smi_init(struct smi_info *new_smi) if (rv) { dev_err(new_smi->io.dev, "Unable to create proc entry: %d\n", rv); - goto out_err_stop_timer; + goto out_err; } #endif @@ -2239,56 +2222,8 @@ static int try_smi_init(struct smi_info *new_smi) return 0; -out_err_remove_attrs: - device_remove_group(new_smi->io.dev, &ipmi_si_dev_attr_group); - dev_set_drvdata(new_smi->io.dev, NULL); - -out_err_stop_timer: - stop_timer_and_thread(new_smi); - out_err: - new_smi->interrupt_disabled = true; - - if (new_smi->intf) { - ipmi_smi_t intf = new_smi->intf; - new_smi->intf = NULL; - ipmi_unregister_smi(intf); - } - - if (new_smi->io.irq_cleanup) { - new_smi->io.irq_cleanup(&new_smi->io); - new_smi->io.irq_cleanup = NULL; - } - - /* - * Wait until we know that we are out of any interrupt - * handlers might have been running before we freed the - * interrupt. - */ - synchronize_sched(); - - if (new_smi->si_sm) { - if (new_smi->handlers) - new_smi->handlers->cleanup(new_smi->si_sm); - kfree(new_smi->si_sm); - new_smi->si_sm = NULL; - } - if (new_smi->io.addr_source_cleanup) { - new_smi->io.addr_source_cleanup(&new_smi->io); - new_smi->io.addr_source_cleanup = NULL; - } - if (new_smi->io.io_cleanup) { - new_smi->io.io_cleanup(&new_smi->io); - new_smi->io.io_cleanup = NULL; - } - - if (new_smi->pdev) { - if (platform_device_registered) - platform_device_unregister(new_smi->pdev); - else - platform_device_put(new_smi->pdev); - new_smi->pdev = NULL; - } + shutdown_one_si(new_smi); kfree(init_name); @@ -2366,17 +2301,14 @@ skip_fallback_noirq: } module_init(init_ipmi_si); -static void cleanup_one_si(struct smi_info *to_clean) +static void shutdown_one_si(struct smi_info *smi_info) { int rv = 0; - if (!to_clean) - return; - - if (to_clean->intf) { - ipmi_smi_t intf = to_clean->intf; + if (smi_info->intf) { + ipmi_smi_t intf = smi_info->intf; - to_clean->intf = NULL; + smi_info->intf = NULL; rv = ipmi_unregister_smi(intf); if (rv) { pr_err(PFX "Unable to unregister device: errno=%d\n", @@ -2384,49 +2316,79 @@ static void cleanup_one_si(struct smi_info *to_clean) } } - device_remove_group(to_clean->io.dev, &ipmi_si_dev_attr_group); - dev_set_drvdata(to_clean->io.dev, NULL); - - list_del(&to_clean->link); + if (smi_info->dev_group_added) { + device_remove_group(smi_info->io.dev, &ipmi_si_dev_attr_group); + smi_info->dev_group_added = false; + } + if (smi_info->io.dev) + dev_set_drvdata(smi_info->io.dev, NULL); /* * Make sure that interrupts, the timer and the thread are * stopped and will not run again. */ - if (to_clean->io.irq_cleanup) - to_clean->io.irq_cleanup(&to_clean->io); - stop_timer_and_thread(to_clean); + smi_info->interrupt_disabled = true; + if (smi_info->io.irq_cleanup) { + smi_info->io.irq_cleanup(&smi_info->io); + smi_info->io.irq_cleanup = NULL; + } + stop_timer_and_thread(smi_info); + + /* + * Wait until we know that we are out of any interrupt + * handlers might have been running before we freed the + * interrupt. + */ + synchronize_sched(); /* * Timeouts are stopped, now make sure the interrupts are off * in the BMC. Note that timers and CPU interrupts are off, * so no need for locks. */ - while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { - poll(to_clean); + while (smi_info->curr_msg || (smi_info->si_state != SI_NORMAL)) { + poll(smi_info); schedule_timeout_uninterruptible(1); } - if (to_clean->handlers) - disable_si_irq(to_clean); - while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { - poll(to_clean); + if (smi_info->handlers) + disable_si_irq(smi_info); + while (smi_info->curr_msg || (smi_info->si_state != SI_NORMAL)) { + poll(smi_info); schedule_timeout_uninterruptible(1); } + if (smi_info->handlers) + smi_info->handlers->cleanup(smi_info->si_sm); + + if (smi_info->io.addr_source_cleanup) { + smi_info->io.addr_source_cleanup(&smi_info->io); + smi_info->io.addr_source_cleanup = NULL; + } + if (smi_info->io.io_cleanup) { + smi_info->io.io_cleanup(&smi_info->io); + smi_info->io.io_cleanup = NULL; + } - if (to_clean->handlers) - to_clean->handlers->cleanup(to_clean->si_sm); + kfree(smi_info->si_sm); + smi_info->si_sm = NULL; +} - kfree(to_clean->si_sm); +static void cleanup_one_si(struct smi_info *smi_info) +{ + if (!smi_info) + return; - if (to_clean->io.addr_source_cleanup) - to_clean->io.addr_source_cleanup(&to_clean->io); - if (to_clean->io.io_cleanup) - to_clean->io.io_cleanup(&to_clean->io); + list_del(&smi_info->link); - if (to_clean->pdev) - platform_device_unregister(to_clean->pdev); + shutdown_one_si(smi_info); + + if (smi_info->pdev) { + if (smi_info->pdev_registered) + platform_device_unregister(smi_info->pdev); + else + platform_device_put(smi_info->pdev); + } - kfree(to_clean); + kfree(smi_info); } int ipmi_si_remove_by_dev(struct device *dev) diff --git a/drivers/char/ipmi/ipmi_si_mem_io.c b/drivers/char/ipmi/ipmi_si_mem_io.c index 8796396ecd0f..1b869d530884 100644 --- a/drivers/char/ipmi/ipmi_si_mem_io.c +++ b/drivers/char/ipmi/ipmi_si_mem_io.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ #include <linux/io.h> #include "ipmi_si.h" diff --git a/drivers/char/ipmi/ipmi_si_parisc.c b/drivers/char/ipmi/ipmi_si_parisc.c index 6b10f0e18a95..f3c99820f564 100644 --- a/drivers/char/ipmi/ipmi_si_parisc.c +++ b/drivers/char/ipmi/ipmi_si_parisc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ #include <linux/module.h> #include <asm/hardware.h> /* for register_parisc_driver() stuff */ diff --git a/drivers/char/ipmi/ipmi_si_pci.c b/drivers/char/ipmi/ipmi_si_pci.c index 27dd11c49d21..f54ca6869ed2 100644 --- a/drivers/char/ipmi/ipmi_si_pci.c +++ b/drivers/char/ipmi/ipmi_si_pci.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_si_pci.c * @@ -17,16 +18,12 @@ module_param_named(trypci, si_trypci, bool, 0); MODULE_PARM_DESC(trypci, "Setting this to zero will disable the" " default scan of the interfaces identified via pci"); -#define PCI_ERMC_CLASSCODE 0x0C0700 -#define PCI_ERMC_CLASSCODE_MASK 0xffffff00 -#define PCI_ERMC_CLASSCODE_TYPE_MASK 0xff -#define PCI_ERMC_CLASSCODE_TYPE_SMIC 0x00 -#define PCI_ERMC_CLASSCODE_TYPE_KCS 0x01 -#define PCI_ERMC_CLASSCODE_TYPE_BT 0x02 +#define PCI_CLASS_SERIAL_IPMI 0x0c07 +#define PCI_CLASS_SERIAL_IPMI_SMIC 0x0c0700 +#define PCI_CLASS_SERIAL_IPMI_KCS 0x0c0701 +#define PCI_CLASS_SERIAL_IPMI_BT 0x0c0702 -#define PCI_HP_VENDOR_ID 0x103C -#define PCI_MMC_DEVICE_ID 0x121A -#define PCI_MMC_ADDR_CW 0x10 +#define PCI_DEVICE_ID_HP_MMC 0x121A static void ipmi_pci_cleanup(struct si_sm_io *io) { @@ -65,32 +62,43 @@ static int ipmi_pci_probe_regspacing(struct si_sm_io *io) return DEFAULT_REGSPACING; } +static struct pci_device_id ipmi_pci_blacklist[] = { + /* + * This is a "Virtual IPMI device", whatever that is. It appears + * as a KCS device by the class, but it is not one. + */ + { PCI_VDEVICE(REALTEK, 0x816c) }, + { 0, } +}; + static int ipmi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int rv; - int class_type = pdev->class & PCI_ERMC_CLASSCODE_TYPE_MASK; struct si_sm_io io; + if (pci_match_id(ipmi_pci_blacklist, pdev)) + return -ENODEV; + memset(&io, 0, sizeof(io)); io.addr_source = SI_PCI; dev_info(&pdev->dev, "probing via PCI"); - switch (class_type) { - case PCI_ERMC_CLASSCODE_TYPE_SMIC: + switch (pdev->class) { + case PCI_CLASS_SERIAL_IPMI_SMIC: io.si_type = SI_SMIC; break; - case PCI_ERMC_CLASSCODE_TYPE_KCS: + case PCI_CLASS_SERIAL_IPMI_KCS: io.si_type = SI_KCS; break; - case PCI_ERMC_CLASSCODE_TYPE_BT: + case PCI_CLASS_SERIAL_IPMI_BT: io.si_type = SI_BT; break; default: - dev_info(&pdev->dev, "Unknown IPMI type: %d\n", class_type); + dev_info(&pdev->dev, "Unknown IPMI class: %x\n", pdev->class); return -ENOMEM; } @@ -138,8 +146,10 @@ static void ipmi_pci_remove(struct pci_dev *pdev) } static const struct pci_device_id ipmi_pci_devices[] = { - { PCI_DEVICE(PCI_HP_VENDOR_ID, PCI_MMC_DEVICE_ID) }, - { PCI_DEVICE_CLASS(PCI_ERMC_CLASSCODE, PCI_ERMC_CLASSCODE_MASK) }, + { PCI_VDEVICE(HP, PCI_DEVICE_ID_HP_MMC) }, + { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_IPMI_SMIC, ~0) }, + { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_IPMI_KCS, ~0) }, + { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_IPMI_BT, ~0) }, { 0, } }; MODULE_DEVICE_TABLE(pci, ipmi_pci_devices); diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c index f4214870d726..bf69927502bd 100644 --- a/drivers/char/ipmi/ipmi_si_platform.c +++ b/drivers/char/ipmi/ipmi_si_platform.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_si_platform.c * @@ -50,14 +51,6 @@ MODULE_PARM_DESC(trydmi, "Setting this to zero will disable the" #endif #ifdef CONFIG_ACPI - -/* - * Once we get an ACPI failure, we don't try any more, because we go - * through the tables sequentially. Once we don't find a table, there - * are no more. - */ -static int acpi_failure; - /* For GPE-type interrupts. */ static u32 ipmi_acpi_gpe(acpi_handle gpe_device, u32 gpe_number, void *context) @@ -102,146 +95,6 @@ static int acpi_gpe_irq_setup(struct si_sm_io *io) return 0; } } - -/* - * Defined at - * http://h21007.www2.hp.com/portal/download/files/unprot/hpspmi.pdf - */ -struct SPMITable { - s8 Signature[4]; - u32 Length; - u8 Revision; - u8 Checksum; - s8 OEMID[6]; - s8 OEMTableID[8]; - s8 OEMRevision[4]; - s8 CreatorID[4]; - s8 CreatorRevision[4]; - u8 InterfaceType; - u8 IPMIlegacy; - s16 SpecificationRevision; - - /* - * Bit 0 - SCI interrupt supported - * Bit 1 - I/O APIC/SAPIC - */ - u8 InterruptType; - - /* - * If bit 0 of InterruptType is set, then this is the SCI - * interrupt in the GPEx_STS register. - */ - u8 GPE; - - s16 Reserved; - - /* - * If bit 1 of InterruptType is set, then this is the I/O - * APIC/SAPIC interrupt. - */ - u32 GlobalSystemInterrupt; - - /* The actual register address. */ - struct acpi_generic_address addr; - - u8 UID[4]; - - s8 spmi_id[1]; /* A '\0' terminated array starts here. */ -}; - -static int try_init_spmi(struct SPMITable *spmi) -{ - struct si_sm_io io; - - if (spmi->IPMIlegacy != 1) { - pr_info(PFX "Bad SPMI legacy %d\n", spmi->IPMIlegacy); - return -ENODEV; - } - - memset(&io, 0, sizeof(io)); - io.addr_source = SI_SPMI; - pr_info(PFX "probing via SPMI\n"); - - /* Figure out the interface type. */ - switch (spmi->InterfaceType) { - case 1: /* KCS */ - io.si_type = SI_KCS; - break; - case 2: /* SMIC */ - io.si_type = SI_SMIC; - break; - case 3: /* BT */ - io.si_type = SI_BT; - break; - case 4: /* SSIF, just ignore */ - return -EIO; - default: - pr_info(PFX "Unknown ACPI/SPMI SI type %d\n", - spmi->InterfaceType); - return -EIO; - } - - if (spmi->InterruptType & 1) { - /* We've got a GPE interrupt. */ - io.irq = spmi->GPE; - io.irq_setup = acpi_gpe_irq_setup; - } else if (spmi->InterruptType & 2) { - /* We've got an APIC/SAPIC interrupt. */ - io.irq = spmi->GlobalSystemInterrupt; - io.irq_setup = ipmi_std_irq_setup; - } else { - /* Use the default interrupt setting. */ - io.irq = 0; - io.irq_setup = NULL; - } - - if (spmi->addr.bit_width) { - /* A (hopefully) properly formed register bit width. */ - io.regspacing = spmi->addr.bit_width / 8; - } else { - io.regspacing = DEFAULT_REGSPACING; - } - io.regsize = io.regspacing; - io.regshift = spmi->addr.bit_offset; - - if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { - io.addr_type = IPMI_MEM_ADDR_SPACE; - } else if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_IO) { - io.addr_type = IPMI_IO_ADDR_SPACE; - } else { - pr_warn(PFX "Unknown ACPI I/O Address type\n"); - return -EIO; - } - io.addr_data = spmi->addr.address; - - pr_info("ipmi_si: SPMI: %s %#lx regsize %d spacing %d irq %d\n", - (io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem", - io.addr_data, io.regsize, io.regspacing, io.irq); - - return ipmi_si_add_smi(&io); -} - -static void spmi_find_bmc(void) -{ - acpi_status status; - struct SPMITable *spmi; - int i; - - if (acpi_disabled) - return; - - if (acpi_failure) - return; - - for (i = 0; ; i++) { - status = acpi_get_table(ACPI_SIG_SPMI, i+1, - (struct acpi_table_header **)&spmi); - if (status != AE_OK) - return; - - try_init_spmi(spmi); - } -} #endif static struct resource * @@ -579,12 +432,6 @@ void ipmi_si_platform_init(void) int rv = platform_driver_register(&ipmi_platform_driver); if (rv) pr_err(PFX "Unable to register driver: %d\n", rv); - -#ifdef CONFIG_ACPI - if (si_tryacpi) - spmi_find_bmc(); -#endif - } void ipmi_si_platform_shutdown(void) diff --git a/drivers/char/ipmi/ipmi_si_port_io.c b/drivers/char/ipmi/ipmi_si_port_io.c index e5ce174fbeeb..ef6dffcea9fa 100644 --- a/drivers/char/ipmi/ipmi_si_port_io.c +++ b/drivers/char/ipmi/ipmi_si_port_io.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ #include <linux/io.h> #include "ipmi_si.h" diff --git a/drivers/char/ipmi/ipmi_si_sm.h b/drivers/char/ipmi/ipmi_si_sm.h index aa8d88ab4433..aaddf047d923 100644 --- a/drivers/char/ipmi/ipmi_si_sm.h +++ b/drivers/char/ipmi/ipmi_si_sm.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * ipmi_si_sm.h * @@ -11,27 +12,6 @@ * source@mvista.com * * Copyright 2002 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/ipmi.h> diff --git a/drivers/char/ipmi/ipmi_smic_sm.c b/drivers/char/ipmi/ipmi_smic_sm.c index 8f7c73ff58f2..466a5aac5298 100644 --- a/drivers/char/ipmi/ipmi_smic_sm.c +++ b/drivers/char/ipmi/ipmi_smic_sm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_smic_sm.c * @@ -18,28 +19,7 @@ * copyright notice: * (c) Copyright 2001 Grant Grundler (c) Copyright * 2001 Hewlett-Packard Company - * - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ + */ #include <linux/kernel.h> /* For printk. */ #include <linux/string.h> diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index f929e72bdac8..35a82f4bfd78 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_ssif.c * @@ -13,11 +14,6 @@ * * Copyright 2003 Intel Corporation * Copyright 2005 MontaVista Software - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ /* @@ -761,7 +757,7 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ssif_info->ssif_state = SSIF_NORMAL; ipmi_ssif_unlock_cond(ssif_info, flags); pr_warn(PFX "Error getting flags: %d %d, %x\n", - result, len, data[2]); + result, len, (len >= 3) ? data[2] : 0); } else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || data[1] != IPMI_GET_MSG_FLAGS_CMD) { /* @@ -783,7 +779,7 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, if ((result < 0) || (len < 3) || (data[2] != 0)) { /* Error clearing flags */ pr_warn(PFX "Error clearing flags: %d %d, %x\n", - result, len, data[2]); + result, len, (len >= 3) ? data[2] : 0); } else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || data[1] != IPMI_CLEAR_MSG_FLAGS_CMD) { pr_warn(PFX "Invalid response clearing flags: %x %x\n", @@ -1906,108 +1902,6 @@ static const struct acpi_device_id ssif_acpi_match[] = { { }, }; MODULE_DEVICE_TABLE(acpi, ssif_acpi_match); - -/* - * Once we get an ACPI failure, we don't try any more, because we go - * through the tables sequentially. Once we don't find a table, there - * are no more. - */ -static int acpi_failure; - -/* - * Defined in the IPMI 2.0 spec. - */ -struct SPMITable { - s8 Signature[4]; - u32 Length; - u8 Revision; - u8 Checksum; - s8 OEMID[6]; - s8 OEMTableID[8]; - s8 OEMRevision[4]; - s8 CreatorID[4]; - s8 CreatorRevision[4]; - u8 InterfaceType; - u8 IPMIlegacy; - s16 SpecificationRevision; - - /* - * Bit 0 - SCI interrupt supported - * Bit 1 - I/O APIC/SAPIC - */ - u8 InterruptType; - - /* - * If bit 0 of InterruptType is set, then this is the SCI - * interrupt in the GPEx_STS register. - */ - u8 GPE; - - s16 Reserved; - - /* - * If bit 1 of InterruptType is set, then this is the I/O - * APIC/SAPIC interrupt. - */ - u32 GlobalSystemInterrupt; - - /* The actual register address. */ - struct acpi_generic_address addr; - - u8 UID[4]; - - s8 spmi_id[1]; /* A '\0' terminated array starts here. */ -}; - -static int try_init_spmi(struct SPMITable *spmi) -{ - unsigned short myaddr; - - if (num_addrs >= MAX_SSIF_BMCS) - return -1; - - if (spmi->IPMIlegacy != 1) { - pr_warn("IPMI: Bad SPMI legacy: %d\n", spmi->IPMIlegacy); - return -ENODEV; - } - - if (spmi->InterfaceType != 4) - return -ENODEV; - - if (spmi->addr.space_id != ACPI_ADR_SPACE_SMBUS) { - pr_warn(PFX "Invalid ACPI SSIF I/O Address type: %d\n", - spmi->addr.space_id); - return -EIO; - } - - myaddr = spmi->addr.address & 0x7f; - - return new_ssif_client(myaddr, NULL, 0, 0, SI_SPMI, NULL); -} - -static void spmi_find_bmc(void) -{ - acpi_status status; - struct SPMITable *spmi; - int i; - - if (acpi_disabled) - return; - - if (acpi_failure) - return; - - for (i = 0; ; i++) { - status = acpi_get_table(ACPI_SIG_SPMI, i+1, - (struct acpi_table_header **)&spmi); - if (status != AE_OK) - return; - - try_init_spmi(spmi); - } -} -#else -static void spmi_find_bmc(void) { } #endif #ifdef CONFIG_DMI @@ -2112,9 +2006,6 @@ static int init_ipmi_ssif(void) ssif_i2c_driver.driver.acpi_match_table = ACPI_PTR(ssif_acpi_match); - if (ssif_tryacpi) - spmi_find_bmc(); - if (ssif_trydmi) { rv = platform_driver_register(&ipmi_driver); if (rv) diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index a58acdcf7414..22bc287eac2d 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_watchdog.c * @@ -8,27 +9,6 @@ * source@mvista.com * * Copyright 2002 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/module.h> diff --git a/drivers/char/ipmi/kcs_bmc.c b/drivers/char/ipmi/kcs_bmc.c new file mode 100644 index 000000000000..fbfc05e3f3d1 --- /dev/null +++ b/drivers/char/ipmi/kcs_bmc.c @@ -0,0 +1,467 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2015-2018, Intel Corporation. + */ + +#define pr_fmt(fmt) "kcs-bmc: " fmt + +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/ipmi_bmc.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/poll.h> +#include <linux/sched.h> +#include <linux/slab.h> + +#include "kcs_bmc.h" + +#define KCS_MSG_BUFSIZ 1000 + +#define KCS_ZERO_DATA 0 + + +/* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */ +#define KCS_STATUS_STATE(state) (state << 6) +#define KCS_STATUS_STATE_MASK GENMASK(7, 6) +#define KCS_STATUS_CMD_DAT BIT(3) +#define KCS_STATUS_SMS_ATN BIT(2) +#define KCS_STATUS_IBF BIT(1) +#define KCS_STATUS_OBF BIT(0) + +/* IPMI 2.0 - Table 9-2, KCS Interface State Bits */ +enum kcs_states { + IDLE_STATE = 0, + READ_STATE = 1, + WRITE_STATE = 2, + ERROR_STATE = 3, +}; + +/* IPMI 2.0 - Table 9-3, KCS Interface Control Codes */ +#define KCS_CMD_GET_STATUS_ABORT 0x60 +#define KCS_CMD_WRITE_START 0x61 +#define KCS_CMD_WRITE_END 0x62 +#define KCS_CMD_READ_BYTE 0x68 + +static inline u8 read_data(struct kcs_bmc *kcs_bmc) +{ + return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr); +} + +static inline void write_data(struct kcs_bmc *kcs_bmc, u8 data) +{ + kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data); +} + +static inline u8 read_status(struct kcs_bmc *kcs_bmc) +{ + return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.str); +} + +static inline void write_status(struct kcs_bmc *kcs_bmc, u8 data) +{ + kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data); +} + +static void update_status_bits(struct kcs_bmc *kcs_bmc, u8 mask, u8 val) +{ + u8 tmp = read_status(kcs_bmc); + + tmp &= ~mask; + tmp |= val & mask; + + write_status(kcs_bmc, tmp); +} + +static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state) +{ + update_status_bits(kcs_bmc, KCS_STATUS_STATE_MASK, + KCS_STATUS_STATE(state)); +} + +static void kcs_force_abort(struct kcs_bmc *kcs_bmc) +{ + set_state(kcs_bmc, ERROR_STATE); + read_data(kcs_bmc); + write_data(kcs_bmc, KCS_ZERO_DATA); + + kcs_bmc->phase = KCS_PHASE_ERROR; + kcs_bmc->data_in_avail = false; + kcs_bmc->data_in_idx = 0; +} + +static void kcs_bmc_handle_data(struct kcs_bmc *kcs_bmc) +{ + u8 data; + + switch (kcs_bmc->phase) { + case KCS_PHASE_WRITE_START: + kcs_bmc->phase = KCS_PHASE_WRITE_DATA; + /* fall through */ + + case KCS_PHASE_WRITE_DATA: + if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) { + set_state(kcs_bmc, WRITE_STATE); + write_data(kcs_bmc, KCS_ZERO_DATA); + kcs_bmc->data_in[kcs_bmc->data_in_idx++] = + read_data(kcs_bmc); + } else { + kcs_force_abort(kcs_bmc); + kcs_bmc->error = KCS_LENGTH_ERROR; + } + break; + + case KCS_PHASE_WRITE_END_CMD: + if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) { + set_state(kcs_bmc, READ_STATE); + kcs_bmc->data_in[kcs_bmc->data_in_idx++] = + read_data(kcs_bmc); + kcs_bmc->phase = KCS_PHASE_WRITE_DONE; + kcs_bmc->data_in_avail = true; + wake_up_interruptible(&kcs_bmc->queue); + } else { + kcs_force_abort(kcs_bmc); + kcs_bmc->error = KCS_LENGTH_ERROR; + } + break; + + case KCS_PHASE_READ: + if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) + set_state(kcs_bmc, IDLE_STATE); + + data = read_data(kcs_bmc); + if (data != KCS_CMD_READ_BYTE) { + set_state(kcs_bmc, ERROR_STATE); + write_data(kcs_bmc, KCS_ZERO_DATA); + break; + } + + if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) { + write_data(kcs_bmc, KCS_ZERO_DATA); + kcs_bmc->phase = KCS_PHASE_IDLE; + break; + } + + write_data(kcs_bmc, + kcs_bmc->data_out[kcs_bmc->data_out_idx++]); + break; + + case KCS_PHASE_ABORT_ERROR1: + set_state(kcs_bmc, READ_STATE); + read_data(kcs_bmc); + write_data(kcs_bmc, kcs_bmc->error); + kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2; + break; + + case KCS_PHASE_ABORT_ERROR2: + set_state(kcs_bmc, IDLE_STATE); + read_data(kcs_bmc); + write_data(kcs_bmc, KCS_ZERO_DATA); + kcs_bmc->phase = KCS_PHASE_IDLE; + break; + + default: + kcs_force_abort(kcs_bmc); + break; + } +} + +static void kcs_bmc_handle_cmd(struct kcs_bmc *kcs_bmc) +{ + u8 cmd; + + set_state(kcs_bmc, WRITE_STATE); + write_data(kcs_bmc, KCS_ZERO_DATA); + + cmd = read_data(kcs_bmc); + switch (cmd) { + case KCS_CMD_WRITE_START: + kcs_bmc->phase = KCS_PHASE_WRITE_START; + kcs_bmc->error = KCS_NO_ERROR; + kcs_bmc->data_in_avail = false; + kcs_bmc->data_in_idx = 0; + break; + + case KCS_CMD_WRITE_END: + if (kcs_bmc->phase != KCS_PHASE_WRITE_DATA) { + kcs_force_abort(kcs_bmc); + break; + } + + kcs_bmc->phase = KCS_PHASE_WRITE_END_CMD; + break; + + case KCS_CMD_GET_STATUS_ABORT: + if (kcs_bmc->error == KCS_NO_ERROR) + kcs_bmc->error = KCS_ABORTED_BY_COMMAND; + + kcs_bmc->phase = KCS_PHASE_ABORT_ERROR1; + kcs_bmc->data_in_avail = false; + kcs_bmc->data_in_idx = 0; + break; + + default: + kcs_force_abort(kcs_bmc); + kcs_bmc->error = KCS_ILLEGAL_CONTROL_CODE; + break; + } +} + +int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc) +{ + unsigned long flags; + int ret = 0; + u8 status; + + spin_lock_irqsave(&kcs_bmc->lock, flags); + + if (!kcs_bmc->running) { + kcs_force_abort(kcs_bmc); + ret = -ENODEV; + goto out_unlock; + } + + status = read_status(kcs_bmc) & (KCS_STATUS_IBF | KCS_STATUS_CMD_DAT); + + switch (status) { + case KCS_STATUS_IBF | KCS_STATUS_CMD_DAT: + kcs_bmc_handle_cmd(kcs_bmc); + break; + + case KCS_STATUS_IBF: + kcs_bmc_handle_data(kcs_bmc); + break; + + default: + ret = -ENODATA; + break; + } + +out_unlock: + spin_unlock_irqrestore(&kcs_bmc->lock, flags); + + return ret; +} +EXPORT_SYMBOL(kcs_bmc_handle_event); + +static inline struct kcs_bmc *to_kcs_bmc(struct file *filp) +{ + return container_of(filp->private_data, struct kcs_bmc, miscdev); +} + +static int kcs_bmc_open(struct inode *inode, struct file *filp) +{ + struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); + int ret = 0; + + spin_lock_irq(&kcs_bmc->lock); + if (!kcs_bmc->running) + kcs_bmc->running = 1; + else + ret = -EBUSY; + spin_unlock_irq(&kcs_bmc->lock); + + return ret; +} + +static __poll_t kcs_bmc_poll(struct file *filp, poll_table *wait) +{ + struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); + __poll_t mask = 0; + + poll_wait(filp, &kcs_bmc->queue, wait); + + spin_lock_irq(&kcs_bmc->lock); + if (kcs_bmc->data_in_avail) + mask |= EPOLLIN; + spin_unlock_irq(&kcs_bmc->lock); + + return mask; +} + +static ssize_t kcs_bmc_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); + bool data_avail; + size_t data_len; + ssize_t ret; + + if (!(filp->f_flags & O_NONBLOCK)) + wait_event_interruptible(kcs_bmc->queue, + kcs_bmc->data_in_avail); + + mutex_lock(&kcs_bmc->mutex); + + spin_lock_irq(&kcs_bmc->lock); + data_avail = kcs_bmc->data_in_avail; + if (data_avail) { + data_len = kcs_bmc->data_in_idx; + memcpy(kcs_bmc->kbuffer, kcs_bmc->data_in, data_len); + } + spin_unlock_irq(&kcs_bmc->lock); + + if (!data_avail) { + ret = -EAGAIN; + goto out_unlock; + } + + if (count < data_len) { + pr_err("channel=%u with too large data : %zu\n", + kcs_bmc->channel, data_len); + + spin_lock_irq(&kcs_bmc->lock); + kcs_force_abort(kcs_bmc); + spin_unlock_irq(&kcs_bmc->lock); + + ret = -EOVERFLOW; + goto out_unlock; + } + + if (copy_to_user(buf, kcs_bmc->kbuffer, data_len)) { + ret = -EFAULT; + goto out_unlock; + } + + ret = data_len; + + spin_lock_irq(&kcs_bmc->lock); + if (kcs_bmc->phase == KCS_PHASE_WRITE_DONE) { + kcs_bmc->phase = KCS_PHASE_WAIT_READ; + kcs_bmc->data_in_avail = false; + kcs_bmc->data_in_idx = 0; + } else { + ret = -EAGAIN; + } + spin_unlock_irq(&kcs_bmc->lock); + +out_unlock: + mutex_unlock(&kcs_bmc->mutex); + + return ret; +} + +static ssize_t kcs_bmc_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); + ssize_t ret; + + /* a minimum response size '3' : netfn + cmd + ccode */ + if (count < 3 || count > KCS_MSG_BUFSIZ) + return -EINVAL; + + mutex_lock(&kcs_bmc->mutex); + + if (copy_from_user(kcs_bmc->kbuffer, buf, count)) { + ret = -EFAULT; + goto out_unlock; + } + + spin_lock_irq(&kcs_bmc->lock); + if (kcs_bmc->phase == KCS_PHASE_WAIT_READ) { + kcs_bmc->phase = KCS_PHASE_READ; + kcs_bmc->data_out_idx = 1; + kcs_bmc->data_out_len = count; + memcpy(kcs_bmc->data_out, kcs_bmc->kbuffer, count); + write_data(kcs_bmc, kcs_bmc->data_out[0]); + ret = count; + } else { + ret = -EINVAL; + } + spin_unlock_irq(&kcs_bmc->lock); + +out_unlock: + mutex_unlock(&kcs_bmc->mutex); + + return ret; +} + +static long kcs_bmc_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); + long ret = 0; + + spin_lock_irq(&kcs_bmc->lock); + + switch (cmd) { + case IPMI_BMC_IOCTL_SET_SMS_ATN: + update_status_bits(kcs_bmc, KCS_STATUS_SMS_ATN, + KCS_STATUS_SMS_ATN); + break; + + case IPMI_BMC_IOCTL_CLEAR_SMS_ATN: + update_status_bits(kcs_bmc, KCS_STATUS_SMS_ATN, + 0); + break; + + case IPMI_BMC_IOCTL_FORCE_ABORT: + kcs_force_abort(kcs_bmc); + break; + + default: + ret = -EINVAL; + break; + } + + spin_unlock_irq(&kcs_bmc->lock); + + return ret; +} + +static int kcs_bmc_release(struct inode *inode, struct file *filp) +{ + struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp); + + spin_lock_irq(&kcs_bmc->lock); + kcs_bmc->running = 0; + kcs_force_abort(kcs_bmc); + spin_unlock_irq(&kcs_bmc->lock); + + return 0; +} + +static const struct file_operations kcs_bmc_fops = { + .owner = THIS_MODULE, + .open = kcs_bmc_open, + .read = kcs_bmc_read, + .write = kcs_bmc_write, + .release = kcs_bmc_release, + .poll = kcs_bmc_poll, + .unlocked_ioctl = kcs_bmc_ioctl, +}; + +struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel) +{ + struct kcs_bmc *kcs_bmc; + + kcs_bmc = devm_kzalloc(dev, sizeof(*kcs_bmc) + sizeof_priv, GFP_KERNEL); + if (!kcs_bmc) + return NULL; + + dev_set_name(dev, "ipmi-kcs%u", channel); + + spin_lock_init(&kcs_bmc->lock); + kcs_bmc->channel = channel; + + mutex_init(&kcs_bmc->mutex); + init_waitqueue_head(&kcs_bmc->queue); + + kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); + kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); + kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); + if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer) + return NULL; + + kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR; + kcs_bmc->miscdev.name = dev_name(dev); + kcs_bmc->miscdev.fops = &kcs_bmc_fops; + + return kcs_bmc; +} +EXPORT_SYMBOL(kcs_bmc_alloc); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); +MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software"); diff --git a/drivers/char/ipmi/kcs_bmc.h b/drivers/char/ipmi/kcs_bmc.h new file mode 100644 index 000000000000..eb9ea4ce78b8 --- /dev/null +++ b/drivers/char/ipmi/kcs_bmc.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2015-2018, Intel Corporation. + */ + +#ifndef __KCS_BMC_H__ +#define __KCS_BMC_H__ + +#include <linux/miscdevice.h> + +/* Different phases of the KCS BMC module. + * KCS_PHASE_IDLE: + * BMC should not be expecting nor sending any data. + * KCS_PHASE_WRITE_START: + * BMC is receiving a WRITE_START command from system software. + * KCS_PHASE_WRITE_DATA: + * BMC is receiving a data byte from system software. + * KCS_PHASE_WRITE_END_CMD: + * BMC is waiting a last data byte from system software. + * KCS_PHASE_WRITE_DONE: + * BMC has received the whole request from system software. + * KCS_PHASE_WAIT_READ: + * BMC is waiting the response from the upper IPMI service. + * KCS_PHASE_READ: + * BMC is transferring the response to system software. + * KCS_PHASE_ABORT_ERROR1: + * BMC is waiting error status request from system software. + * KCS_PHASE_ABORT_ERROR2: + * BMC is waiting for idle status afer error from system software. + * KCS_PHASE_ERROR: + * BMC has detected a protocol violation at the interface level. + */ +enum kcs_phases { + KCS_PHASE_IDLE, + + KCS_PHASE_WRITE_START, + KCS_PHASE_WRITE_DATA, + KCS_PHASE_WRITE_END_CMD, + KCS_PHASE_WRITE_DONE, + + KCS_PHASE_WAIT_READ, + KCS_PHASE_READ, + + KCS_PHASE_ABORT_ERROR1, + KCS_PHASE_ABORT_ERROR2, + KCS_PHASE_ERROR +}; + +/* IPMI 2.0 - Table 9-4, KCS Interface Status Codes */ +enum kcs_errors { + KCS_NO_ERROR = 0x00, + KCS_ABORTED_BY_COMMAND = 0x01, + KCS_ILLEGAL_CONTROL_CODE = 0x02, + KCS_LENGTH_ERROR = 0x06, + KCS_UNSPECIFIED_ERROR = 0xFF +}; + +/* IPMI 2.0 - 9.5, KCS Interface Registers + * @idr: Input Data Register + * @odr: Output Data Register + * @str: Status Register + */ +struct kcs_ioreg { + u32 idr; + u32 odr; + u32 str; +}; + +struct kcs_bmc { + spinlock_t lock; + + u32 channel; + int running; + + /* Setup by BMC KCS controller driver */ + struct kcs_ioreg ioreg; + u8 (*io_inputb)(struct kcs_bmc *kcs_bmc, u32 reg); + void (*io_outputb)(struct kcs_bmc *kcs_bmc, u32 reg, u8 b); + + enum kcs_phases phase; + enum kcs_errors error; + + wait_queue_head_t queue; + bool data_in_avail; + int data_in_idx; + u8 *data_in; + + int data_out_idx; + int data_out_len; + u8 *data_out; + + struct mutex mutex; + u8 *kbuffer; + + struct miscdevice miscdev; + + unsigned long priv[]; +}; + +static inline void *kcs_bmc_priv(struct kcs_bmc *kcs_bmc) +{ + return kcs_bmc->priv; +} + +int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc); +struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, + u32 channel); +#endif /* __KCS_BMC_H__ */ diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c new file mode 100644 index 000000000000..3c955946e647 --- /dev/null +++ b/drivers/char/ipmi/kcs_bmc_aspeed.c @@ -0,0 +1,320 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2015-2018, Intel Corporation. + */ + +#define pr_fmt(fmt) "aspeed-kcs-bmc: " fmt + +#include <linux/atomic.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/poll.h> +#include <linux/regmap.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/timer.h> + +#include "kcs_bmc.h" + + +#define DEVICE_NAME "ast-kcs-bmc" + +#define KCS_CHANNEL_MAX 4 + +/* mapped to lpc-bmc@0 IO space */ +#define LPC_HICR0 0x000 +#define LPC_HICR0_LPC3E BIT(7) +#define LPC_HICR0_LPC2E BIT(6) +#define LPC_HICR0_LPC1E BIT(5) +#define LPC_HICR2 0x008 +#define LPC_HICR2_IBFIF3 BIT(3) +#define LPC_HICR2_IBFIF2 BIT(2) +#define LPC_HICR2_IBFIF1 BIT(1) +#define LPC_HICR4 0x010 +#define LPC_HICR4_LADR12AS BIT(7) +#define LPC_HICR4_KCSENBL BIT(2) +#define LPC_LADR3H 0x014 +#define LPC_LADR3L 0x018 +#define LPC_LADR12H 0x01C +#define LPC_LADR12L 0x020 +#define LPC_IDR1 0x024 +#define LPC_IDR2 0x028 +#define LPC_IDR3 0x02C +#define LPC_ODR1 0x030 +#define LPC_ODR2 0x034 +#define LPC_ODR3 0x038 +#define LPC_STR1 0x03C +#define LPC_STR2 0x040 +#define LPC_STR3 0x044 + +/* mapped to lpc-host@80 IO space */ +#define LPC_HICRB 0x080 +#define LPC_HICRB_IBFIF4 BIT(1) +#define LPC_HICRB_LPC4E BIT(0) +#define LPC_LADR4 0x090 +#define LPC_IDR4 0x094 +#define LPC_ODR4 0x098 +#define LPC_STR4 0x09C + +struct aspeed_kcs_bmc { + struct regmap *map; +}; + + +static u8 aspeed_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg) +{ + struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + u32 val = 0; + int rc; + + rc = regmap_read(priv->map, reg, &val); + WARN(rc != 0, "regmap_read() failed: %d\n", rc); + + return rc == 0 ? (u8) val : 0; +} + +static void aspeed_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data) +{ + struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + int rc; + + rc = regmap_write(priv->map, reg, data); + WARN(rc != 0, "regmap_write() failed: %d\n", rc); +} + + +/* + * AST_usrGuide_KCS.pdf + * 2. Background: + * we note D for Data, and C for Cmd/Status, default rules are + * A. KCS1 / KCS2 ( D / C:X / X+4 ) + * D / C : CA0h / CA4h + * D / C : CA8h / CACh + * B. KCS3 ( D / C:XX2h / XX3h ) + * D / C : CA2h / CA3h + * D / C : CB2h / CB3h + * C. KCS4 + * D / C : CA4h / CA5h + */ +static void aspeed_kcs_set_address(struct kcs_bmc *kcs_bmc, u16 addr) +{ + struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + + switch (kcs_bmc->channel) { + case 1: + regmap_update_bits(priv->map, LPC_HICR4, + LPC_HICR4_LADR12AS, 0); + regmap_write(priv->map, LPC_LADR12H, addr >> 8); + regmap_write(priv->map, LPC_LADR12L, addr & 0xFF); + break; + + case 2: + regmap_update_bits(priv->map, LPC_HICR4, + LPC_HICR4_LADR12AS, LPC_HICR4_LADR12AS); + regmap_write(priv->map, LPC_LADR12H, addr >> 8); + regmap_write(priv->map, LPC_LADR12L, addr & 0xFF); + break; + + case 3: + regmap_write(priv->map, LPC_LADR3H, addr >> 8); + regmap_write(priv->map, LPC_LADR3L, addr & 0xFF); + break; + + case 4: + regmap_write(priv->map, LPC_LADR4, ((addr + 1) << 16) | + addr); + break; + + default: + break; + } +} + +static void aspeed_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable) +{ + struct aspeed_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); + + switch (kcs_bmc->channel) { + case 1: + if (enable) { + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF1, LPC_HICR2_IBFIF1); + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC1E, LPC_HICR0_LPC1E); + } else { + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC1E, 0); + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF1, 0); + } + break; + + case 2: + if (enable) { + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF2, LPC_HICR2_IBFIF2); + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC2E, LPC_HICR0_LPC2E); + } else { + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC2E, 0); + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF2, 0); + } + break; + + case 3: + if (enable) { + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF3, LPC_HICR2_IBFIF3); + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC3E, LPC_HICR0_LPC3E); + regmap_update_bits(priv->map, LPC_HICR4, + LPC_HICR4_KCSENBL, LPC_HICR4_KCSENBL); + } else { + regmap_update_bits(priv->map, LPC_HICR0, + LPC_HICR0_LPC3E, 0); + regmap_update_bits(priv->map, LPC_HICR4, + LPC_HICR4_KCSENBL, 0); + regmap_update_bits(priv->map, LPC_HICR2, + LPC_HICR2_IBFIF3, 0); + } + break; + + case 4: + if (enable) + regmap_update_bits(priv->map, LPC_HICRB, + LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E, + LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E); + else + regmap_update_bits(priv->map, LPC_HICRB, + LPC_HICRB_IBFIF4 | LPC_HICRB_LPC4E, + 0); + break; + + default: + break; + } +} + +static irqreturn_t aspeed_kcs_irq(int irq, void *arg) +{ + struct kcs_bmc *kcs_bmc = arg; + + if (!kcs_bmc_handle_event(kcs_bmc)) + return IRQ_HANDLED; + + return IRQ_NONE; +} + +static int aspeed_kcs_config_irq(struct kcs_bmc *kcs_bmc, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + return devm_request_irq(dev, irq, aspeed_kcs_irq, IRQF_SHARED, + dev_name(dev), kcs_bmc); +} + +static const struct kcs_ioreg ast_kcs_bmc_ioregs[KCS_CHANNEL_MAX] = { + { .idr = LPC_IDR1, .odr = LPC_ODR1, .str = LPC_STR1 }, + { .idr = LPC_IDR2, .odr = LPC_ODR2, .str = LPC_STR2 }, + { .idr = LPC_IDR3, .odr = LPC_ODR3, .str = LPC_STR3 }, + { .idr = LPC_IDR4, .odr = LPC_ODR4, .str = LPC_STR4 }, +}; + +static int aspeed_kcs_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct aspeed_kcs_bmc *priv; + struct kcs_bmc *kcs_bmc; + u32 chan, addr; + int rc; + + rc = of_property_read_u32(dev->of_node, "kcs_chan", &chan); + if ((rc != 0) || (chan == 0 || chan > KCS_CHANNEL_MAX)) { + dev_err(dev, "no valid 'kcs_chan' configured\n"); + return -ENODEV; + } + + rc = of_property_read_u32(dev->of_node, "kcs_addr", &addr); + if (rc) { + dev_err(dev, "no valid 'kcs_addr' configured\n"); + return -ENODEV; + } + + kcs_bmc = kcs_bmc_alloc(dev, sizeof(*priv), chan); + if (!kcs_bmc) + return -ENOMEM; + + priv = kcs_bmc_priv(kcs_bmc); + priv->map = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(priv->map)) { + dev_err(dev, "Couldn't get regmap\n"); + return -ENODEV; + } + + kcs_bmc->ioreg = ast_kcs_bmc_ioregs[chan - 1]; + kcs_bmc->io_inputb = aspeed_kcs_inb; + kcs_bmc->io_outputb = aspeed_kcs_outb; + + dev_set_drvdata(dev, kcs_bmc); + + aspeed_kcs_set_address(kcs_bmc, addr); + aspeed_kcs_enable_channel(kcs_bmc, true); + rc = aspeed_kcs_config_irq(kcs_bmc, pdev); + if (rc) + return rc; + + rc = misc_register(&kcs_bmc->miscdev); + if (rc) { + dev_err(dev, "Unable to register device\n"); + return rc; + } + + pr_info("channel=%u addr=0x%x idr=0x%x odr=0x%x str=0x%x\n", + chan, addr, + kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str); + + return 0; +} + +static int aspeed_kcs_remove(struct platform_device *pdev) +{ + struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev); + + misc_deregister(&kcs_bmc->miscdev); + + return 0; +} + +static const struct of_device_id ast_kcs_bmc_match[] = { + { .compatible = "aspeed,ast2400-kcs-bmc" }, + { .compatible = "aspeed,ast2500-kcs-bmc" }, + { } +}; +MODULE_DEVICE_TABLE(of, ast_kcs_bmc_match); + +static struct platform_driver ast_kcs_bmc_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = ast_kcs_bmc_match, + }, + .probe = aspeed_kcs_probe, + .remove = aspeed_kcs_remove, +}; +module_platform_driver(ast_kcs_bmc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>"); +MODULE_DESCRIPTION("Aspeed device interface to the KCS BMC device"); diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 052011bcf100..ffeb60d3434c 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -137,7 +137,7 @@ static ssize_t read_mem(struct file *file, char __user *buf, while (count > 0) { unsigned long remaining; - int allowed; + int allowed, probe; sz = size_inside_page(p, count); @@ -160,9 +160,9 @@ static ssize_t read_mem(struct file *file, char __user *buf, if (!ptr) goto failed; - err = probe_kernel_read(bounce, ptr, sz); + probe = probe_kernel_read(bounce, ptr, sz); unxlate_dev_mem_ptr(p, ptr); - if (err) + if (probe) goto failed; remaining = copy_to_user(buf, bounce, sz); diff --git a/drivers/char/random.c b/drivers/char/random.c index e5b3d3ba4660..e027e7fa1472 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -709,7 +709,8 @@ retry: } /* should we wake readers? */ - if (entropy_bits >= random_read_wakeup_bits) { + if (entropy_bits >= random_read_wakeup_bits && + wq_has_sleeper(&random_read_wait)) { wake_up_interruptible(&random_read_wait); kill_fasync(&fasync, SIGIO, POLL_IN); } @@ -732,7 +733,7 @@ retry: static int credit_entropy_bits_safe(struct entropy_store *r, int nbits) { - const int nbits_max = (int)(~0U >> (ENTROPY_SHIFT + 1)); + const int nbits_max = r->poolinfo->poolwords * 32; if (nbits < 0) return -EINVAL; @@ -963,7 +964,6 @@ static ssize_t extract_crng_user(void __user *buf, size_t nbytes) struct timer_rand_state { cycles_t last_time; long last_delta, last_delta2; - unsigned dont_count_entropy:1; }; #define INIT_TIMER_RAND_STATE { INITIAL_JIFFIES, }; @@ -1029,35 +1029,33 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num) * We take into account the first, second and third-order deltas * in order to make our estimate. */ + delta = sample.jiffies - state->last_time; + state->last_time = sample.jiffies; + + delta2 = delta - state->last_delta; + state->last_delta = delta; + + delta3 = delta2 - state->last_delta2; + state->last_delta2 = delta2; + + if (delta < 0) + delta = -delta; + if (delta2 < 0) + delta2 = -delta2; + if (delta3 < 0) + delta3 = -delta3; + if (delta > delta2) + delta = delta2; + if (delta > delta3) + delta = delta3; - if (!state->dont_count_entropy) { - delta = sample.jiffies - state->last_time; - state->last_time = sample.jiffies; - - delta2 = delta - state->last_delta; - state->last_delta = delta; - - delta3 = delta2 - state->last_delta2; - state->last_delta2 = delta2; - - if (delta < 0) - delta = -delta; - if (delta2 < 0) - delta2 = -delta2; - if (delta3 < 0) - delta3 = -delta3; - if (delta > delta2) - delta = delta2; - if (delta > delta3) - delta = delta3; + /* + * delta is now minimum absolute delta. + * Round down by 1 bit on general principles, + * and limit entropy entimate to 12 bits. + */ + credit_entropy_bits(r, min_t(int, fls(delta>>1), 11)); - /* - * delta is now minimum absolute delta. - * Round down by 1 bit on general principles, - * and limit entropy entimate to 12 bits. - */ - credit_entropy_bits(r, min_t(int, fls(delta>>1), 11)); - } preempt_enable(); } diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index 0c858d027bf3..57dc546628b5 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -809,89 +809,6 @@ static __poll_t rtc_poll(struct file *file, poll_table *wait) } #endif -int rtc_register(rtc_task_t *task) -{ -#ifndef RTC_IRQ - return -EIO; -#else - if (task == NULL || task->func == NULL) - return -EINVAL; - spin_lock_irq(&rtc_lock); - if (rtc_status & RTC_IS_OPEN) { - spin_unlock_irq(&rtc_lock); - return -EBUSY; - } - spin_lock(&rtc_task_lock); - if (rtc_callback) { - spin_unlock(&rtc_task_lock); - spin_unlock_irq(&rtc_lock); - return -EBUSY; - } - rtc_status |= RTC_IS_OPEN; - rtc_callback = task; - spin_unlock(&rtc_task_lock); - spin_unlock_irq(&rtc_lock); - return 0; -#endif -} -EXPORT_SYMBOL(rtc_register); - -int rtc_unregister(rtc_task_t *task) -{ -#ifndef RTC_IRQ - return -EIO; -#else - unsigned char tmp; - - spin_lock_irq(&rtc_lock); - spin_lock(&rtc_task_lock); - if (rtc_callback != task) { - spin_unlock(&rtc_task_lock); - spin_unlock_irq(&rtc_lock); - return -ENXIO; - } - rtc_callback = NULL; - - /* disable controls */ - if (!hpet_mask_rtc_irq_bit(RTC_PIE | RTC_AIE | RTC_UIE)) { - tmp = CMOS_READ(RTC_CONTROL); - tmp &= ~RTC_PIE; - tmp &= ~RTC_AIE; - tmp &= ~RTC_UIE; - CMOS_WRITE(tmp, RTC_CONTROL); - CMOS_READ(RTC_INTR_FLAGS); - } - if (rtc_status & RTC_TIMER_ON) { - rtc_status &= ~RTC_TIMER_ON; - del_timer(&rtc_irq_timer); - } - rtc_status &= ~RTC_IS_OPEN; - spin_unlock(&rtc_task_lock); - spin_unlock_irq(&rtc_lock); - return 0; -#endif -} -EXPORT_SYMBOL(rtc_unregister); - -int rtc_control(rtc_task_t *task, unsigned int cmd, unsigned long arg) -{ -#ifndef RTC_IRQ - return -EIO; -#else - unsigned long flags; - if (cmd != RTC_PIE_ON && cmd != RTC_PIE_OFF && cmd != RTC_IRQP_SET) - return -EINVAL; - spin_lock_irqsave(&rtc_task_lock, flags); - if (rtc_callback != task) { - spin_unlock_irqrestore(&rtc_task_lock, flags); - return -ENXIO; - } - spin_unlock_irqrestore(&rtc_task_lock, flags); - return rtc_do_ioctl(cmd, arg, 1); -#endif -} -EXPORT_SYMBOL(rtc_control); - /* * The various file operations we support. */ diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c deleted file mode 100644 index 3d4cca64b2d4..000000000000 --- a/drivers/char/tile-srom.c +++ /dev/null @@ -1,475 +0,0 @@ -/* - * Copyright 2011 Tilera Corporation. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, version 2. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for - * more details. - * - * SPI Flash ROM driver - * - * This source code is derived from code provided in "Linux Device - * Drivers, Third Edition", by Jonathan Corbet, Alessandro Rubini, and - * Greg Kroah-Hartman, published by O'Reilly Media, Inc. - */ - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/kernel.h> /* printk() */ -#include <linux/slab.h> /* kmalloc() */ -#include <linux/fs.h> /* everything... */ -#include <linux/errno.h> /* error codes */ -#include <linux/types.h> /* size_t */ -#include <linux/proc_fs.h> -#include <linux/fcntl.h> /* O_ACCMODE */ -#include <linux/pagemap.h> -#include <linux/hugetlb.h> -#include <linux/uaccess.h> -#include <linux/platform_device.h> -#include <hv/hypervisor.h> -#include <linux/ioctl.h> -#include <linux/cdev.h> -#include <linux/delay.h> -#include <hv/drv_srom_intf.h> - -/* - * Size of our hypervisor I/O requests. We break up large transfers - * so that we don't spend large uninterrupted spans of time in the - * hypervisor. Erasing an SROM sector takes a significant fraction of - * a second, so if we allowed the user to, say, do one I/O to write the - * entire ROM, we'd get soft lockup timeouts, or worse. - */ -#define SROM_CHUNK_SIZE ((size_t)4096) - -/* - * When hypervisor is busy (e.g. erasing), poll the status periodically. - */ - -/* - * Interval to poll the state in msec - */ -#define SROM_WAIT_TRY_INTERVAL 20 - -/* - * Maximum times to poll the state - */ -#define SROM_MAX_WAIT_TRY_TIMES 1000 - -struct srom_dev { - int hv_devhdl; /* Handle for hypervisor device */ - u32 total_size; /* Size of this device */ - u32 sector_size; /* Size of a sector */ - u32 page_size; /* Size of a page */ - struct mutex lock; /* Allow only one accessor at a time */ -}; - -static int srom_major; /* Dynamic major by default */ -module_param(srom_major, int, 0); -MODULE_AUTHOR("Tilera Corporation"); -MODULE_LICENSE("GPL"); - -static int srom_devs; /* Number of SROM partitions */ -static struct cdev srom_cdev; -static struct platform_device *srom_parent; -static struct class *srom_class; -static struct srom_dev *srom_devices; - -/* - * Handle calling the hypervisor and managing EAGAIN/EBUSY. - */ - -static ssize_t _srom_read(int hv_devhdl, void *buf, - loff_t off, size_t count) -{ - int retval, retries = SROM_MAX_WAIT_TRY_TIMES; - for (;;) { - retval = hv_dev_pread(hv_devhdl, 0, (HV_VirtAddr)buf, - count, off); - if (retval >= 0) - return retval; - if (retval == HV_EAGAIN) - continue; - if (retval == HV_EBUSY && --retries > 0) { - msleep(SROM_WAIT_TRY_INTERVAL); - continue; - } - pr_err("_srom_read: error %d\n", retval); - return -EIO; - } -} - -static ssize_t _srom_write(int hv_devhdl, const void *buf, - loff_t off, size_t count) -{ - int retval, retries = SROM_MAX_WAIT_TRY_TIMES; - for (;;) { - retval = hv_dev_pwrite(hv_devhdl, 0, (HV_VirtAddr)buf, - count, off); - if (retval >= 0) - return retval; - if (retval == HV_EAGAIN) - continue; - if (retval == HV_EBUSY && --retries > 0) { - msleep(SROM_WAIT_TRY_INTERVAL); - continue; - } - pr_err("_srom_write: error %d\n", retval); - return -EIO; - } -} - -/** - * srom_open() - Device open routine. - * @inode: Inode for this device. - * @filp: File for this specific open of the device. - * - * Returns zero, or an error code. - */ -static int srom_open(struct inode *inode, struct file *filp) -{ - filp->private_data = &srom_devices[iminor(inode)]; - return 0; -} - - -/** - * srom_release() - Device release routine. - * @inode: Inode for this device. - * @filp: File for this specific open of the device. - * - * Returns zero, or an error code. - */ -static int srom_release(struct inode *inode, struct file *filp) -{ - struct srom_dev *srom = filp->private_data; - char dummy; - - /* Make sure we've flushed anything written to the ROM. */ - mutex_lock(&srom->lock); - if (srom->hv_devhdl >= 0) - _srom_write(srom->hv_devhdl, &dummy, SROM_FLUSH_OFF, 1); - mutex_unlock(&srom->lock); - - filp->private_data = NULL; - - return 0; -} - - -/** - * srom_read() - Read data from the device. - * @filp: File for this specific open of the device. - * @buf: User's data buffer. - * @count: Number of bytes requested. - * @f_pos: File position. - * - * Returns number of bytes read, or an error code. - */ -static ssize_t srom_read(struct file *filp, char __user *buf, - size_t count, loff_t *f_pos) -{ - int retval = 0; - void *kernbuf; - struct srom_dev *srom = filp->private_data; - - kernbuf = kmalloc(SROM_CHUNK_SIZE, GFP_KERNEL); - if (!kernbuf) - return -ENOMEM; - - if (mutex_lock_interruptible(&srom->lock)) { - retval = -ERESTARTSYS; - kfree(kernbuf); - return retval; - } - - while (count) { - int hv_retval; - int bytes_this_pass = min(count, SROM_CHUNK_SIZE); - - hv_retval = _srom_read(srom->hv_devhdl, kernbuf, - *f_pos, bytes_this_pass); - if (hv_retval <= 0) { - if (retval == 0) - retval = hv_retval; - break; - } - - if (copy_to_user(buf, kernbuf, hv_retval) != 0) { - retval = -EFAULT; - break; - } - - retval += hv_retval; - *f_pos += hv_retval; - buf += hv_retval; - count -= hv_retval; - } - - mutex_unlock(&srom->lock); - kfree(kernbuf); - - return retval; -} - -/** - * srom_write() - Write data to the device. - * @filp: File for this specific open of the device. - * @buf: User's data buffer. - * @count: Number of bytes requested. - * @f_pos: File position. - * - * Returns number of bytes written, or an error code. - */ -static ssize_t srom_write(struct file *filp, const char __user *buf, - size_t count, loff_t *f_pos) -{ - int retval = 0; - void *kernbuf; - struct srom_dev *srom = filp->private_data; - - kernbuf = kmalloc(SROM_CHUNK_SIZE, GFP_KERNEL); - if (!kernbuf) - return -ENOMEM; - - if (mutex_lock_interruptible(&srom->lock)) { - retval = -ERESTARTSYS; - kfree(kernbuf); - return retval; - } - - while (count) { - int hv_retval; - int bytes_this_pass = min(count, SROM_CHUNK_SIZE); - - if (copy_from_user(kernbuf, buf, bytes_this_pass) != 0) { - retval = -EFAULT; - break; - } - - hv_retval = _srom_write(srom->hv_devhdl, kernbuf, - *f_pos, bytes_this_pass); - if (hv_retval <= 0) { - if (retval == 0) - retval = hv_retval; - break; - } - - retval += hv_retval; - *f_pos += hv_retval; - buf += hv_retval; - count -= hv_retval; - } - - mutex_unlock(&srom->lock); - kfree(kernbuf); - - return retval; -} - -/* Provide our own implementation so we can use srom->total_size. */ -loff_t srom_llseek(struct file *file, loff_t offset, int origin) -{ - struct srom_dev *srom = file->private_data; - return fixed_size_llseek(file, offset, origin, srom->total_size); -} - -static ssize_t total_size_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct srom_dev *srom = dev_get_drvdata(dev); - return sprintf(buf, "%u\n", srom->total_size); -} -static DEVICE_ATTR_RO(total_size); - -static ssize_t sector_size_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct srom_dev *srom = dev_get_drvdata(dev); - return sprintf(buf, "%u\n", srom->sector_size); -} -static DEVICE_ATTR_RO(sector_size); - -static ssize_t page_size_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct srom_dev *srom = dev_get_drvdata(dev); - return sprintf(buf, "%u\n", srom->page_size); -} -static DEVICE_ATTR_RO(page_size); - -static struct attribute *srom_dev_attrs[] = { - &dev_attr_total_size.attr, - &dev_attr_sector_size.attr, - &dev_attr_page_size.attr, - NULL, -}; -ATTRIBUTE_GROUPS(srom_dev); - -static char *srom_devnode(struct device *dev, umode_t *mode) -{ - if (mode) - *mode = 0644; - return kasprintf(GFP_KERNEL, "srom/%s", dev_name(dev)); -} - -/* - * The fops - */ -static const struct file_operations srom_fops = { - .owner = THIS_MODULE, - .llseek = srom_llseek, - .read = srom_read, - .write = srom_write, - .open = srom_open, - .release = srom_release, -}; - -/** - * srom_setup_minor() - Initialize per-minor information. - * @srom: Per-device SROM state. - * @devhdl: Partition device handle. - */ -static int srom_setup_minor(struct srom_dev *srom, int devhdl) -{ - srom->hv_devhdl = devhdl; - mutex_init(&srom->lock); - - if (_srom_read(devhdl, &srom->total_size, - SROM_TOTAL_SIZE_OFF, sizeof(srom->total_size)) < 0) - return -EIO; - if (_srom_read(devhdl, &srom->sector_size, - SROM_SECTOR_SIZE_OFF, sizeof(srom->sector_size)) < 0) - return -EIO; - if (_srom_read(devhdl, &srom->page_size, - SROM_PAGE_SIZE_OFF, sizeof(srom->page_size)) < 0) - return -EIO; - - return 0; -} - -/** srom_init() - Initialize the driver's module. */ -static int srom_init(void) -{ - int result, i; - dev_t dev = MKDEV(srom_major, 0); - - /* - * Start with a plausible number of partitions; the krealloc() call - * below will yield about log(srom_devs) additional allocations. - */ - srom_devices = kmalloc(4 * sizeof(struct srom_dev), GFP_KERNEL); - - /* Discover the number of srom partitions. */ - for (i = 0; ; i++) { - int devhdl; - char buf[20]; - struct srom_dev *new_srom_devices = - krealloc(srom_devices, (i+1) * sizeof(struct srom_dev), - GFP_KERNEL); - if (!new_srom_devices) { - result = -ENOMEM; - goto fail_mem; - } - srom_devices = new_srom_devices; - sprintf(buf, "srom/0/%d", i); - devhdl = hv_dev_open((HV_VirtAddr)buf, 0); - if (devhdl < 0) { - if (devhdl != HV_ENODEV) - pr_notice("srom/%d: hv_dev_open failed: %d.\n", - i, devhdl); - break; - } - result = srom_setup_minor(&srom_devices[i], devhdl); - if (result != 0) - goto fail_mem; - } - srom_devs = i; - - /* Bail out early if we have no partitions at all. */ - if (srom_devs == 0) { - result = -ENODEV; - goto fail_mem; - } - - /* Register our major, and accept a dynamic number. */ - if (srom_major) - result = register_chrdev_region(dev, srom_devs, "srom"); - else { - result = alloc_chrdev_region(&dev, 0, srom_devs, "srom"); - srom_major = MAJOR(dev); - } - if (result < 0) - goto fail_mem; - - /* Register a character device. */ - cdev_init(&srom_cdev, &srom_fops); - srom_cdev.owner = THIS_MODULE; - srom_cdev.ops = &srom_fops; - result = cdev_add(&srom_cdev, dev, srom_devs); - if (result < 0) - goto fail_chrdev; - - /* Create a parent device */ - srom_parent = platform_device_register_simple("srom", -1, NULL, 0); - if (IS_ERR(srom_parent)) { - result = PTR_ERR(srom_parent); - goto fail_pdev; - } - - /* Create a sysfs class. */ - srom_class = class_create(THIS_MODULE, "srom"); - if (IS_ERR(srom_class)) { - result = PTR_ERR(srom_class); - goto fail_cdev; - } - srom_class->dev_groups = srom_dev_groups; - srom_class->devnode = srom_devnode; - - /* Create per-partition devices */ - for (i = 0; i < srom_devs; i++) { - struct device *dev = - device_create(srom_class, &srom_parent->dev, - MKDEV(srom_major, i), srom_devices + i, - "%d", i); - result = PTR_ERR_OR_ZERO(dev); - if (result < 0) - goto fail_class; - } - - return 0; - -fail_class: - for (i = 0; i < srom_devs; i++) - device_destroy(srom_class, MKDEV(srom_major, i)); - class_destroy(srom_class); -fail_cdev: - platform_device_unregister(srom_parent); -fail_pdev: - cdev_del(&srom_cdev); -fail_chrdev: - unregister_chrdev_region(dev, srom_devs); -fail_mem: - kfree(srom_devices); - return result; -} - -/** srom_cleanup() - Clean up the driver's module. */ -static void srom_cleanup(void) -{ - int i; - for (i = 0; i < srom_devs; i++) - device_destroy(srom_class, MKDEV(srom_major, i)); - class_destroy(srom_class); - cdev_del(&srom_cdev); - platform_device_unregister(srom_parent); - unregister_chrdev_region(MKDEV(srom_major, 0), srom_devs); - kfree(srom_devices); -} - -module_init(srom_init); -module_exit(srom_cleanup); diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c index 4d1dc8b46877..f95b9c75175b 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.c +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -457,7 +457,7 @@ static int st33zp24_recv(struct tpm_chip *chip, unsigned char *buf, size_t count) { int size = 0; - int expected; + u32 expected; if (!chip) return -EBUSY; @@ -474,7 +474,7 @@ static int st33zp24_recv(struct tpm_chip *chip, unsigned char *buf, } expected = be32_to_cpu(*(__be32 *)(buf + 2)); - if (expected > count) { + if (expected > count || expected < TPM_HEADER_SIZE) { size = -EIO; goto out; } diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 76df4fbcf089..c43a9e28995e 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -369,20 +369,40 @@ err_len: return -EINVAL; } -/** - * tmp_transmit - Internal kernel interface to transmit TPM commands. - * - * @chip: TPM chip to use - * @buf: TPM command buffer - * @bufsiz: length of the TPM command buffer - * @flags: tpm transmit flags - bitmap - * - * Return: - * 0 when the operation is successful. - * A negative number for system errors (errno). - */ -ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, - u8 *buf, size_t bufsiz, unsigned int flags) +static int tpm_request_locality(struct tpm_chip *chip) +{ + int rc; + + if (!chip->ops->request_locality) + return 0; + + rc = chip->ops->request_locality(chip, 0); + if (rc < 0) + return rc; + + chip->locality = rc; + + return 0; +} + +static void tpm_relinquish_locality(struct tpm_chip *chip) +{ + int rc; + + if (!chip->ops->relinquish_locality) + return; + + rc = chip->ops->relinquish_locality(chip, chip->locality); + if (rc) + dev_err(&chip->dev, "%s: : error %d\n", __func__, rc); + + chip->locality = -1; +} + +static ssize_t tpm_try_transmit(struct tpm_chip *chip, + struct tpm_space *space, + u8 *buf, size_t bufsiz, + unsigned int flags) { struct tpm_output_header *header = (void *)buf; int rc; @@ -422,8 +442,6 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, if (!(flags & TPM_TRANSMIT_UNLOCKED)) mutex_lock(&chip->tpm_mutex); - if (chip->dev.parent) - pm_runtime_get_sync(chip->dev.parent); if (chip->ops->clk_enable != NULL) chip->ops->clk_enable(chip, true); @@ -431,19 +449,20 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, /* Store the decision as chip->locality will be changed. */ need_locality = chip->locality == -1; - if (!(flags & TPM_TRANSMIT_RAW) && - need_locality && chip->ops->request_locality) { - rc = chip->ops->request_locality(chip, 0); + if (!(flags & TPM_TRANSMIT_RAW) && need_locality) { + rc = tpm_request_locality(chip); if (rc < 0) goto out_no_locality; - chip->locality = rc; } + if (chip->dev.parent) + pm_runtime_get_sync(chip->dev.parent); + rc = tpm2_prepare_space(chip, space, ordinal, buf); if (rc) goto out; - rc = chip->ops->send(chip, (u8 *) buf, count); + rc = chip->ops->send(chip, buf, count); if (rc < 0) { if (rc != -EPIPE) dev_err(&chip->dev, @@ -480,7 +499,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, goto out; out_recv: - len = chip->ops->recv(chip, (u8 *) buf, bufsiz); + len = chip->ops->recv(chip, buf, bufsiz); if (len < 0) { rc = len; dev_err(&chip->dev, @@ -499,27 +518,95 @@ out_recv: rc = tpm2_commit_space(chip, space, ordinal, buf, &len); out: - if (need_locality && chip->ops->relinquish_locality) { - chip->ops->relinquish_locality(chip, chip->locality); - chip->locality = -1; - } + if (chip->dev.parent) + pm_runtime_put_sync(chip->dev.parent); + + if (need_locality) + tpm_relinquish_locality(chip); + out_no_locality: if (chip->ops->clk_enable != NULL) chip->ops->clk_enable(chip, false); - if (chip->dev.parent) - pm_runtime_put_sync(chip->dev.parent); - if (!(flags & TPM_TRANSMIT_UNLOCKED)) mutex_unlock(&chip->tpm_mutex); return rc ? rc : len; } /** - * tmp_transmit_cmd - send a tpm command to the device + * tpm_transmit - Internal kernel interface to transmit TPM commands. + * + * @chip: TPM chip to use + * @space: tpm space + * @buf: TPM command buffer + * @bufsiz: length of the TPM command buffer + * @flags: tpm transmit flags - bitmap + * + * A wrapper around tpm_try_transmit that handles TPM2_RC_RETRY + * returns from the TPM and retransmits the command after a delay up + * to a maximum wait of TPM2_DURATION_LONG. + * + * Note: TPM1 never returns TPM2_RC_RETRY so the retry logic is TPM2 + * only + * + * Return: + * the length of the return when the operation is successful. + * A negative number for system errors (errno). + */ +ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, + u8 *buf, size_t bufsiz, unsigned int flags) +{ + struct tpm_output_header *header = (struct tpm_output_header *)buf; + /* space for header and handles */ + u8 save[TPM_HEADER_SIZE + 3*sizeof(u32)]; + unsigned int delay_msec = TPM2_DURATION_SHORT; + u32 rc = 0; + ssize_t ret; + const size_t save_size = min(space ? sizeof(save) : TPM_HEADER_SIZE, + bufsiz); + /* the command code is where the return code will be */ + u32 cc = be32_to_cpu(header->return_code); + + /* + * Subtlety here: if we have a space, the handles will be + * transformed, so when we restore the header we also have to + * restore the handles. + */ + memcpy(save, buf, save_size); + + for (;;) { + ret = tpm_try_transmit(chip, space, buf, bufsiz, flags); + if (ret < 0) + break; + rc = be32_to_cpu(header->return_code); + if (rc != TPM2_RC_RETRY && rc != TPM2_RC_TESTING) + break; + /* + * return immediately if self test returns test + * still running to shorten boot time. + */ + if (rc == TPM2_RC_TESTING && cc == TPM2_CC_SELF_TEST) + break; + delay_msec *= 2; + if (delay_msec > TPM2_DURATION_LONG) { + if (rc == TPM2_RC_RETRY) + dev_err(&chip->dev, "in retry loop\n"); + else + dev_err(&chip->dev, + "self test is still running\n"); + break; + } + tpm_msleep(delay_msec); + memcpy(buf, save, save_size); + } + return ret; +} +/** + * tpm_transmit_cmd - send a tpm command to the device * The function extracts tpm out header return code * * @chip: TPM chip to use + * @space: tpm space * @buf: TPM command buffer * @bufsiz: length of the buffer * @min_rsp_body_length: minimum expected length of response body @@ -532,7 +619,7 @@ out_no_locality: * A positive number for a TPM error. */ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space, - const void *buf, size_t bufsiz, + void *buf, size_t bufsiz, size_t min_rsp_body_length, unsigned int flags, const char *desc) { @@ -540,7 +627,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space, int err; ssize_t len; - len = tpm_transmit(chip, space, (u8 *)buf, bufsiz, flags); + len = tpm_transmit(chip, space, buf, bufsiz, flags); if (len < 0) return len; @@ -666,6 +753,8 @@ int tpm_get_timeouts(struct tpm_chip *chip) msecs_to_jiffies(TPM2_DURATION_MEDIUM); chip->duration[TPM_LONG] = msecs_to_jiffies(TPM2_DURATION_LONG); + chip->duration[TPM_LONG_LONG] = + msecs_to_jiffies(TPM2_DURATION_LONG_LONG); chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS; return 0; @@ -754,6 +843,7 @@ int tpm_get_timeouts(struct tpm_chip *chip) usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium)); chip->duration[TPM_LONG] = usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long)); + chip->duration[TPM_LONG_LONG] = 0; /* not used under 1.2 */ /* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above * value wrong and apparently reports msecs rather than usecs. So we @@ -969,6 +1059,10 @@ int tpm_do_selftest(struct tpm_chip *chip) loops = jiffies_to_msecs(duration) / delay_msec; rc = tpm_continue_selftest(chip); + if (rc == TPM_ERR_INVALID_POSTINIT) { + chip->flags |= TPM_CHIP_FLAG_ALWAYS_POWERED; + dev_info(&chip->dev, "TPM not ready (%d)\n", rc); + } /* This may fail if there was no TPM driver during a suspend/resume * cycle; some may return 10 (BAD_ORDINAL), others 28 (FAILEDSELFTEST) */ @@ -1190,6 +1284,10 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max) break; recd = be32_to_cpu(tpm_cmd.params.getrandom_out.rng_data_len); + if (recd > num_bytes) { + total = -EFAULT; + break; + } rlength = be32_to_cpu(tpm_cmd.header.out.length); if (rlength < offsetof(struct tpm_getrandom_out, rng_data) + diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index f895fba4e20d..7f2d0f489e9c 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -67,7 +67,9 @@ enum tpm_duration { TPM_SHORT = 0, TPM_MEDIUM = 1, TPM_LONG = 2, + TPM_LONG_LONG = 3, TPM_UNDEFINED, + TPM_NUM_DURATIONS = TPM_UNDEFINED, }; #define TPM_WARN_RETRY 0x800 @@ -79,15 +81,20 @@ enum tpm_duration { #define TPM_HEADER_SIZE 10 enum tpm2_const { - TPM2_PLATFORM_PCR = 24, - TPM2_PCR_SELECT_MIN = ((TPM2_PLATFORM_PCR + 7) / 8), - TPM2_TIMEOUT_A = 750, - TPM2_TIMEOUT_B = 2000, - TPM2_TIMEOUT_C = 200, - TPM2_TIMEOUT_D = 30, - TPM2_DURATION_SHORT = 20, - TPM2_DURATION_MEDIUM = 750, - TPM2_DURATION_LONG = 2000, + TPM2_PLATFORM_PCR = 24, + TPM2_PCR_SELECT_MIN = ((TPM2_PLATFORM_PCR + 7) / 8), +}; + +enum tpm2_timeouts { + TPM2_TIMEOUT_A = 750, + TPM2_TIMEOUT_B = 2000, + TPM2_TIMEOUT_C = 200, + TPM2_TIMEOUT_D = 30, + TPM2_DURATION_SHORT = 20, + TPM2_DURATION_MEDIUM = 750, + TPM2_DURATION_LONG = 2000, + TPM2_DURATION_LONG_LONG = 300000, + TPM2_DURATION_DEFAULT = 120000, }; enum tpm2_structures { @@ -104,10 +111,12 @@ enum tpm2_return_codes { TPM2_RC_HASH = 0x0083, /* RC_FMT1 */ TPM2_RC_HANDLE = 0x008B, TPM2_RC_INITIALIZE = 0x0100, /* RC_VER1 */ + TPM2_RC_FAILURE = 0x0101, TPM2_RC_DISABLED = 0x0120, TPM2_RC_COMMAND_CODE = 0x0143, TPM2_RC_TESTING = 0x090A, /* RC_WARN */ TPM2_RC_REFERENCE_H0 = 0x0910, + TPM2_RC_RETRY = 0x0922, }; enum tpm2_algorithms { @@ -123,6 +132,7 @@ enum tpm2_algorithms { enum tpm2_command_codes { TPM2_CC_FIRST = 0x011F, + TPM2_CC_CREATE_PRIMARY = 0x0131, TPM2_CC_SELF_TEST = 0x0143, TPM2_CC_STARTUP = 0x0144, TPM2_CC_SHUTDOWN = 0x0145, @@ -227,7 +237,7 @@ struct tpm_chip { unsigned long timeout_c; /* jiffies */ unsigned long timeout_d; /* jiffies */ bool timeout_adjusted; - unsigned long duration[3]; /* jiffies */ + unsigned long duration[TPM_NUM_DURATIONS]; /* jiffies */ bool duration_adjusted; struct dentry *bios_dir[TPM_NUM_EVENT_LOG_FILES]; @@ -506,7 +516,7 @@ enum tpm_transmit_flags { ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, u8 *buf, size_t bufsiz, unsigned int flags); ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space, - const void *buf, size_t bufsiz, + void *buf, size_t bufsiz, size_t min_rsp_body_length, unsigned int flags, const char *desc); int tpm_startup(struct tpm_chip *chip); diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index c17e75348a99..96c77c8e7f40 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -31,10 +31,6 @@ struct tpm2_startup_in { __be16 startup_type; } __packed; -struct tpm2_self_test_in { - u8 full_test; -} __packed; - struct tpm2_get_tpm_pt_in { __be32 cap_id; __be32 property_id; @@ -60,7 +56,6 @@ struct tpm2_get_random_out { union tpm2_cmd_params { struct tpm2_startup_in startup_in; - struct tpm2_self_test_in selftest_in; struct tpm2_get_tpm_pt_in get_tpm_pt_in; struct tpm2_get_tpm_pt_out get_tpm_pt_out; struct tpm2_get_random_in getrandom_in; @@ -90,6 +85,8 @@ static struct tpm2_hash tpm2_hash_map[] = { * of time the chip could take to return the result. The values * of the SHORT, MEDIUM, and LONG durations are taken from the * PC Client Profile (PTP) specification. + * LONG_LONG is for commands that generates keys which empirically + * takes longer time on some systems. */ static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = { TPM_UNDEFINED, /* 11F */ @@ -110,7 +107,7 @@ static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = { TPM_UNDEFINED, /* 12e */ TPM_UNDEFINED, /* 12f */ TPM_UNDEFINED, /* 130 */ - TPM_UNDEFINED, /* 131 */ + TPM_LONG_LONG, /* 131 */ TPM_UNDEFINED, /* 132 */ TPM_UNDEFINED, /* 133 */ TPM_UNDEFINED, /* 134 */ @@ -144,7 +141,7 @@ static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = { TPM_UNDEFINED, /* 150 */ TPM_UNDEFINED, /* 151 */ TPM_UNDEFINED, /* 152 */ - TPM_UNDEFINED, /* 153 */ + TPM_LONG_LONG, /* 153 */ TPM_UNDEFINED, /* 154 */ TPM_UNDEFINED, /* 155 */ TPM_UNDEFINED, /* 156 */ @@ -683,6 +680,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, if (!rc) { data_len = be16_to_cpup( (__be16 *) &buf.data[TPM_HEADER_SIZE + 4]); + if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE + 1) { + rc = -EFAULT; + goto out; + } rlength = be32_to_cpu(((struct tpm2_cmd *)&buf) ->header.out.length); @@ -817,22 +818,12 @@ unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal) duration = chip->duration[index]; if (duration <= 0) - duration = 2 * 60 * HZ; + duration = msecs_to_jiffies(TPM2_DURATION_DEFAULT); return duration; } EXPORT_SYMBOL_GPL(tpm2_calc_ordinal_duration); -#define TPM2_SELF_TEST_IN_SIZE \ - (sizeof(struct tpm_input_header) + \ - sizeof(struct tpm2_self_test_in)) - -static const struct tpm_input_header tpm2_selftest_header = { - .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), - .length = cpu_to_be32(TPM2_SELF_TEST_IN_SIZE), - .ordinal = cpu_to_be32(TPM2_CC_SELF_TEST) -}; - /** * tpm2_do_selftest() - ensure that all self tests have passed * @@ -848,27 +839,24 @@ static const struct tpm_input_header tpm2_selftest_header = { */ static int tpm2_do_selftest(struct tpm_chip *chip) { + struct tpm_buf buf; + int full; int rc; - unsigned int delay_msec = 10; - long duration; - struct tpm2_cmd cmd; - - duration = jiffies_to_msecs( - tpm2_calc_ordinal_duration(chip, TPM2_CC_SELF_TEST)); - while (1) { - cmd.header.in = tpm2_selftest_header; - cmd.params.selftest_in.full_test = 0; - - rc = tpm_transmit_cmd(chip, NULL, &cmd, TPM2_SELF_TEST_IN_SIZE, - 0, 0, "continue selftest"); + for (full = 0; full < 2; full++) { + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST); + if (rc) + return rc; - if (rc != TPM2_RC_TESTING || delay_msec >= duration) - break; + tpm_buf_append_u8(&buf, full); + rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, + "attempting the self test"); + tpm_buf_destroy(&buf); - /* wait longer than before */ - delay_msec *= 2; - tpm_msleep(delay_msec); + if (rc == TPM2_RC_TESTING) + rc = TPM2_RC_SUCCESS; + if (rc == TPM2_RC_INITIALIZE || rc == TPM2_RC_SUCCESS) + return rc; } return rc; @@ -1054,10 +1042,8 @@ int tpm2_auto_startup(struct tpm_chip *chip) goto out; rc = tpm2_do_selftest(chip); - if (rc != 0 && rc != TPM2_RC_INITIALIZE) { - dev_err(&chip->dev, "TPM self test failed\n"); + if (rc && rc != TPM2_RC_INITIALIZE) goto out; - } if (rc == TPM2_RC_INITIALIZE) { rc = tpm_startup(chip); @@ -1065,10 +1051,8 @@ int tpm2_auto_startup(struct tpm_chip *chip) goto out; rc = tpm2_do_selftest(chip); - if (rc) { - dev_err(&chip->dev, "TPM self test failed\n"); + if (rc) goto out; - } } rc = tpm2_get_pcr_allocation(chip); diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 7b3c2a8aa9de..7f78482cd157 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -112,6 +112,25 @@ struct tpm2_crb_smc { u32 smc_func_id; }; +static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, + unsigned long timeout) +{ + ktime_t start; + ktime_t stop; + + start = ktime_get(); + stop = ktime_add(start, ms_to_ktime(timeout)); + + do { + if ((ioread32(reg) & mask) == value) + return true; + + usleep_range(50, 100); + } while (ktime_before(ktime_get(), stop)); + + return ((ioread32(reg) & mask) == value); +} + /** * crb_go_idle - request tpm crb device to go the idle state * @@ -128,7 +147,7 @@ struct tpm2_crb_smc { * * Return: 0 always */ -static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv) +static int crb_go_idle(struct device *dev, struct crb_priv *priv) { if ((priv->sm == ACPI_TPM2_START_METHOD) || (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) || @@ -136,30 +155,17 @@ static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv) return 0; iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->regs_t->ctrl_req); - /* we don't really care when this settles */ + if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_req, + CRB_CTRL_REQ_GO_IDLE/* mask */, + 0, /* value */ + TPM2_TIMEOUT_C)) { + dev_warn(dev, "goIdle timed out\n"); + return -ETIME; + } return 0; } -static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, - unsigned long timeout) -{ - ktime_t start; - ktime_t stop; - - start = ktime_get(); - stop = ktime_add(start, ms_to_ktime(timeout)); - - do { - if ((ioread32(reg) & mask) == value) - return true; - - usleep_range(50, 100); - } while (ktime_before(ktime_get(), stop)); - - return false; -} - /** * crb_cmd_ready - request tpm crb device to enter ready state * @@ -175,8 +181,7 @@ static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, * * Return: 0 on success -ETIME on timeout; */ -static int __maybe_unused crb_cmd_ready(struct device *dev, - struct crb_priv *priv) +static int crb_cmd_ready(struct device *dev, struct crb_priv *priv) { if ((priv->sm == ACPI_TPM2_START_METHOD) || (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) || @@ -195,11 +200,11 @@ static int __maybe_unused crb_cmd_ready(struct device *dev, return 0; } -static int crb_request_locality(struct tpm_chip *chip, int loc) +static int __crb_request_locality(struct device *dev, + struct crb_priv *priv, int loc) { - struct crb_priv *priv = dev_get_drvdata(&chip->dev); u32 value = CRB_LOC_STATE_LOC_ASSIGNED | - CRB_LOC_STATE_TPM_REG_VALID_STS; + CRB_LOC_STATE_TPM_REG_VALID_STS; if (!priv->regs_h) return 0; @@ -207,21 +212,45 @@ static int crb_request_locality(struct tpm_chip *chip, int loc) iowrite32(CRB_LOC_CTRL_REQUEST_ACCESS, &priv->regs_h->loc_ctrl); if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, value, value, TPM2_TIMEOUT_C)) { - dev_warn(&chip->dev, "TPM_LOC_STATE_x.requestAccess timed out\n"); + dev_warn(dev, "TPM_LOC_STATE_x.requestAccess timed out\n"); return -ETIME; } return 0; } -static void crb_relinquish_locality(struct tpm_chip *chip, int loc) +static int crb_request_locality(struct tpm_chip *chip, int loc) { struct crb_priv *priv = dev_get_drvdata(&chip->dev); + return __crb_request_locality(&chip->dev, priv, loc); +} + +static int __crb_relinquish_locality(struct device *dev, + struct crb_priv *priv, int loc) +{ + u32 mask = CRB_LOC_STATE_LOC_ASSIGNED | + CRB_LOC_STATE_TPM_REG_VALID_STS; + u32 value = CRB_LOC_STATE_TPM_REG_VALID_STS; + if (!priv->regs_h) - return; + return 0; iowrite32(CRB_LOC_CTRL_RELINQUISH, &priv->regs_h->loc_ctrl); + if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, mask, value, + TPM2_TIMEOUT_C)) { + dev_warn(dev, "TPM_LOC_STATE_x.requestAccess timed out\n"); + return -ETIME; + } + + return 0; +} + +static int crb_relinquish_locality(struct tpm_chip *chip, int loc) +{ + struct crb_priv *priv = dev_get_drvdata(&chip->dev); + + return __crb_relinquish_locality(&chip->dev, priv, loc); } static u8 crb_status(struct tpm_chip *chip) @@ -442,6 +471,7 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, u32 pa_high, pa_low; u64 cmd_pa; u32 cmd_size; + __le64 __rsp_pa; u64 rsp_pa; u32 rsp_size; int ret; @@ -475,6 +505,10 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, dev_warn(dev, FW_BUG "Bad ACPI memory layout"); } + ret = __crb_request_locality(dev, priv, 0); + if (ret) + return ret; + priv->regs_t = crb_map_res(dev, priv, &io_res, buf->control_address, sizeof(struct crb_regs_tail)); if (IS_ERR(priv->regs_t)) @@ -503,8 +537,8 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, goto out; } - memcpy_fromio(&rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8); - rsp_pa = le64_to_cpu(rsp_pa); + memcpy_fromio(&__rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8); + rsp_pa = le64_to_cpu(__rsp_pa); rsp_size = crb_fixup_cmd_size(dev, &io_res, rsp_pa, ioread32(&priv->regs_t->ctrl_rsp_size)); @@ -531,6 +565,8 @@ out: crb_go_idle(dev, priv); + __crb_relinquish_locality(dev, priv, 0); + return ret; } @@ -588,10 +624,14 @@ static int crb_acpi_add(struct acpi_device *device) chip->acpi_dev_handle = device->handle; chip->flags = TPM_CHIP_FLAG_TPM2; - rc = crb_cmd_ready(dev, priv); + rc = __crb_request_locality(dev, priv, 0); if (rc) return rc; + rc = crb_cmd_ready(dev, priv); + if (rc) + goto out; + pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); @@ -601,12 +641,15 @@ static int crb_acpi_add(struct acpi_device *device) crb_go_idle(dev, priv); pm_runtime_put_noidle(dev); pm_runtime_disable(dev); - return rc; + goto out; } - pm_runtime_put(dev); + pm_runtime_put_sync(dev); - return 0; +out: + __crb_relinquish_locality(dev, priv, 0); + + return rc; } static int crb_acpi_remove(struct acpi_device *device) diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index c1dd39eaaeeb..6116cd05e228 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -473,7 +473,8 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count) { int size = 0; - int expected, status; + int status; + u32 expected; if (count < TPM_HEADER_SIZE) { size = -EIO; @@ -488,7 +489,7 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count) } expected = be32_to_cpu(*(__be32 *)(buf + 2)); - if ((size_t) expected > count) { + if (((size_t) expected > count) || (expected < TPM_HEADER_SIZE)) { size = -EIO; goto out; } diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index c6428771841f..caa86b19c76d 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -281,7 +281,11 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) struct device *dev = chip->dev.parent; struct i2c_client *client = to_i2c_client(dev); s32 rc; - int expected, status, burst_count, retries, size = 0; + int status; + int burst_count; + int retries; + int size = 0; + u32 expected; if (count < TPM_HEADER_SIZE) { i2c_nuvoton_ready(chip); /* return to idle */ @@ -323,7 +327,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) * to machine native */ expected = be32_to_cpu(*(__be32 *) (buf + 2)); - if (expected > count) { + if (expected > count || expected < size) { dev_err(dev, "%s() expected > count\n", __func__); size = -EIO; continue; diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 183a5f54d875..5a1f47b43947 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -143,11 +143,13 @@ static bool check_locality(struct tpm_chip *chip, int l) return false; } -static void release_locality(struct tpm_chip *chip, int l) +static int release_locality(struct tpm_chip *chip, int l) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY); + + return 0; } static int request_locality(struct tpm_chip *chip, int l) @@ -270,7 +272,8 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); int size = 0; - int expected, status; + int status; + u32 expected; if (count < TPM_HEADER_SIZE) { size = -EIO; @@ -285,7 +288,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) } expected = be32_to_cpu(*(__be32 *) (buf + 2)); - if (expected > count) { + if (expected > count || expected < TPM_HEADER_SIZE) { size = -EIO; goto out; } diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index d5c6a2e952b3..f6e1dbe212a7 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -62,10 +62,10 @@ enum tis_defaults { /* Some timeout values are needed before it is known whether the chip is * TPM 1.0 or TPM 2.0. */ -#define TIS_TIMEOUT_A_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_A) -#define TIS_TIMEOUT_B_MAX max(TIS_LONG_TIMEOUT, TPM2_TIMEOUT_B) -#define TIS_TIMEOUT_C_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_C) -#define TIS_TIMEOUT_D_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_D) +#define TIS_TIMEOUT_A_MAX max_t(int, TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_A) +#define TIS_TIMEOUT_B_MAX max_t(int, TIS_LONG_TIMEOUT, TPM2_TIMEOUT_B) +#define TIS_TIMEOUT_C_MAX max_t(int, TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_C) +#define TIS_TIMEOUT_D_MAX max_t(int, TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_D) #define TPM_ACCESS(l) (0x0000 | ((l) << 12)) #define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12)) diff --git a/drivers/char/xillybus/xillybus_pcie.c b/drivers/char/xillybus/xillybus_pcie.c index dff2d1538164..05e5324f60bd 100644 --- a/drivers/char/xillybus/xillybus_pcie.c +++ b/drivers/char/xillybus/xillybus_pcie.c @@ -24,7 +24,6 @@ MODULE_LICENSE("GPL v2"); #define PCI_DEVICE_ID_XILLYBUS 0xebeb -#define PCI_VENDOR_ID_ALTERA 0x1172 #define PCI_VENDOR_ID_ACTEL 0x11aa #define PCI_VENDOR_ID_LATTICE 0x1204 |