diff options
Diffstat (limited to 'drivers')
54 files changed, 2417 insertions, 546 deletions
diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index 981fba56bc18..1621f2f7f129 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -24,8 +24,6 @@ #include <linux/notifier.h> #include <linux/extcon-provider.h> #include <linux/regmap.h> -#include <linux/gpio.h> -#include <linux/gpio/consumer.h> #include <linux/mfd/axp20x.h> /* Power source status register */ @@ -79,11 +77,6 @@ enum axp288_extcon_reg { AXP288_BC_DET_STAT_REG = 0x2f, }; -enum axp288_mux_select { - EXTCON_GPIO_MUX_SEL_PMIC = 0, - EXTCON_GPIO_MUX_SEL_SOC, -}; - enum axp288_extcon_irq { VBUS_FALLING_IRQ = 0, VBUS_RISING_IRQ, @@ -104,10 +97,8 @@ struct axp288_extcon_info { struct device *dev; struct regmap *regmap; struct regmap_irq_chip_data *regmap_irqc; - struct gpio_desc *gpio_mux_cntl; int irq[EXTCON_IRQ_END]; struct extcon_dev *edev; - struct notifier_block extcon_nb; unsigned int previous_cable; }; @@ -197,15 +188,6 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info) } no_vbus: - /* - * If VBUS is absent Connect D+/D- lines to PMIC for BC - * detection. Else connect them to SOC for USB communication. - */ - if (info->gpio_mux_cntl) - gpiod_set_value(info->gpio_mux_cntl, - vbus_attach ? EXTCON_GPIO_MUX_SEL_SOC - : EXTCON_GPIO_MUX_SEL_PMIC); - extcon_set_state_sync(info->edev, info->previous_cable, false); if (info->previous_cable == EXTCON_CHG_USB_SDP) extcon_set_state_sync(info->edev, EXTCON_USB, false); @@ -253,8 +235,7 @@ static int axp288_extcon_probe(struct platform_device *pdev) { struct axp288_extcon_info *info; struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); - struct axp288_extcon_pdata *pdata = pdev->dev.platform_data; - int ret, i, pirq, gpio; + int ret, i, pirq; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) @@ -264,8 +245,6 @@ static int axp288_extcon_probe(struct platform_device *pdev) info->regmap = axp20x->regmap; info->regmap_irqc = axp20x->regmap_irqc; info->previous_cable = EXTCON_NONE; - if (pdata) - info->gpio_mux_cntl = pdata->gpio_mux_cntl; platform_set_drvdata(pdev, info); @@ -286,21 +265,11 @@ static int axp288_extcon_probe(struct platform_device *pdev) return ret; } - /* Set up gpio control for USB Mux */ - if (info->gpio_mux_cntl) { - gpio = desc_to_gpio(info->gpio_mux_cntl); - ret = devm_gpio_request(&pdev->dev, gpio, "USB_MUX"); - if (ret < 0) { - dev_err(&pdev->dev, - "failed to request the gpio=%d\n", gpio); - return ret; - } - gpiod_direction_output(info->gpio_mux_cntl, - EXTCON_GPIO_MUX_SEL_PMIC); - } - for (i = 0; i < EXTCON_IRQ_END; i++) { pirq = platform_get_irq(pdev, i); + if (pirq < 0) + return pirq; + info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq); if (info->irq[i] < 0) { dev_err(&pdev->dev, diff --git a/drivers/extcon/extcon-usbc-cros-ec.c b/drivers/extcon/extcon-usbc-cros-ec.c index 6187f731b29d..6721ab01fe7d 100644 --- a/drivers/extcon/extcon-usbc-cros-ec.c +++ b/drivers/extcon/extcon-usbc-cros-ec.c @@ -34,16 +34,26 @@ struct cros_ec_extcon_info { struct notifier_block notifier; + unsigned int dr; /* data role */ + bool pr; /* power role (true if VBUS enabled) */ bool dp; /* DisplayPort enabled */ bool mux; /* SuperSpeed (usb3) enabled */ unsigned int power_type; }; static const unsigned int usb_type_c_cable[] = { + EXTCON_USB, + EXTCON_USB_HOST, EXTCON_DISP_DP, EXTCON_NONE, }; +enum usb_data_roles { + DR_NONE, + DR_HOST, + DR_DEVICE, +}; + /** * cros_ec_pd_command() - Send a command to the EC. * @info: pointer to struct cros_ec_extcon_info @@ -150,6 +160,7 @@ static int cros_ec_usb_get_role(struct cros_ec_extcon_info *info, pd_control.port = info->port_id; pd_control.role = USB_PD_CTRL_ROLE_NO_CHANGE; pd_control.mux = USB_PD_CTRL_MUX_NO_CHANGE; + pd_control.swap = USB_PD_CTRL_SWAP_NONE; ret = cros_ec_pd_command(info, EC_CMD_USB_PD_CONTROL, 1, &pd_control, sizeof(pd_control), &resp, sizeof(resp)); @@ -183,11 +194,72 @@ static int cros_ec_pd_get_num_ports(struct cros_ec_extcon_info *info) return resp.num_ports; } +static const char *cros_ec_usb_role_string(unsigned int role) +{ + return role == DR_NONE ? "DISCONNECTED" : + (role == DR_HOST ? "DFP" : "UFP"); +} + +static const char *cros_ec_usb_power_type_string(unsigned int type) +{ + switch (type) { + case USB_CHG_TYPE_NONE: + return "USB_CHG_TYPE_NONE"; + case USB_CHG_TYPE_PD: + return "USB_CHG_TYPE_PD"; + case USB_CHG_TYPE_PROPRIETARY: + return "USB_CHG_TYPE_PROPRIETARY"; + case USB_CHG_TYPE_C: + return "USB_CHG_TYPE_C"; + case USB_CHG_TYPE_BC12_DCP: + return "USB_CHG_TYPE_BC12_DCP"; + case USB_CHG_TYPE_BC12_CDP: + return "USB_CHG_TYPE_BC12_CDP"; + case USB_CHG_TYPE_BC12_SDP: + return "USB_CHG_TYPE_BC12_SDP"; + case USB_CHG_TYPE_OTHER: + return "USB_CHG_TYPE_OTHER"; + case USB_CHG_TYPE_VBUS: + return "USB_CHG_TYPE_VBUS"; + case USB_CHG_TYPE_UNKNOWN: + return "USB_CHG_TYPE_UNKNOWN"; + default: + return "USB_CHG_TYPE_UNKNOWN"; + } +} + +static bool cros_ec_usb_power_type_is_wall_wart(unsigned int type, + unsigned int role) +{ + switch (type) { + /* FIXME : Guppy, Donnettes, and other chargers will be miscategorized + * because they identify with USB_CHG_TYPE_C, but we can't return true + * here from that code because that breaks Suzy-Q and other kinds of + * USB Type-C cables and peripherals. + */ + case USB_CHG_TYPE_PROPRIETARY: + case USB_CHG_TYPE_BC12_DCP: + return true; + case USB_CHG_TYPE_PD: + case USB_CHG_TYPE_C: + case USB_CHG_TYPE_BC12_CDP: + case USB_CHG_TYPE_BC12_SDP: + case USB_CHG_TYPE_OTHER: + case USB_CHG_TYPE_VBUS: + case USB_CHG_TYPE_UNKNOWN: + case USB_CHG_TYPE_NONE: + default: + return false; + } +} + static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info, bool force) { struct device *dev = info->dev; int role, power_type; + unsigned int dr = DR_NONE; + bool pr = false; bool polarity = false; bool dp = false; bool mux = false; @@ -206,9 +278,12 @@ static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info, dev_err(dev, "failed getting role err = %d\n", role); return role; } + dev_dbg(dev, "disconnected\n"); } else { int pd_mux_state; + dr = (role & PD_CTRL_RESP_ROLE_DATA) ? DR_HOST : DR_DEVICE; + pr = (role & PD_CTRL_RESP_ROLE_POWER); pd_mux_state = cros_ec_usb_get_pd_mux_state(info); if (pd_mux_state < 0) pd_mux_state = USB_PD_MUX_USB_ENABLED; @@ -216,20 +291,62 @@ static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info, dp = pd_mux_state & USB_PD_MUX_DP_ENABLED; mux = pd_mux_state & USB_PD_MUX_USB_ENABLED; hpd = pd_mux_state & USB_PD_MUX_HPD_IRQ; - } - if (force || info->dp != dp || info->mux != mux || - info->power_type != power_type) { + dev_dbg(dev, + "connected role 0x%x pwr type %d dr %d pr %d pol %d mux %d dp %d hpd %d\n", + role, power_type, dr, pr, polarity, mux, dp, hpd); + } + /* + * When there is no USB host (e.g. USB PD charger), + * we are not really a UFP for the AP. + */ + if (dr == DR_DEVICE && + cros_ec_usb_power_type_is_wall_wart(power_type, role)) + dr = DR_NONE; + + if (force || info->dr != dr || info->pr != pr || info->dp != dp || + info->mux != mux || info->power_type != power_type) { + bool host_connected = false, device_connected = false; + + dev_dbg(dev, "Type/Role switch! type = %s role = %s\n", + cros_ec_usb_power_type_string(power_type), + cros_ec_usb_role_string(dr)); + info->dr = dr; + info->pr = pr; info->dp = dp; info->mux = mux; info->power_type = power_type; - extcon_set_state(info->edev, EXTCON_DISP_DP, dp); + if (dr == DR_DEVICE) + device_connected = true; + else if (dr == DR_HOST) + host_connected = true; + extcon_set_state(info->edev, EXTCON_USB, device_connected); + extcon_set_state(info->edev, EXTCON_USB_HOST, host_connected); + extcon_set_state(info->edev, EXTCON_DISP_DP, dp); + extcon_set_property(info->edev, EXTCON_USB, + EXTCON_PROP_USB_VBUS, + (union extcon_property_value)(int)pr); + extcon_set_property(info->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_VBUS, + (union extcon_property_value)(int)pr); + extcon_set_property(info->edev, EXTCON_USB, + EXTCON_PROP_USB_TYPEC_POLARITY, + (union extcon_property_value)(int)polarity); + extcon_set_property(info->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_TYPEC_POLARITY, + (union extcon_property_value)(int)polarity); extcon_set_property(info->edev, EXTCON_DISP_DP, EXTCON_PROP_USB_TYPEC_POLARITY, (union extcon_property_value)(int)polarity); + extcon_set_property(info->edev, EXTCON_USB, + EXTCON_PROP_USB_SS, + (union extcon_property_value)(int)mux); + extcon_set_property(info->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_SS, + (union extcon_property_value)(int)mux); extcon_set_property(info->edev, EXTCON_DISP_DP, EXTCON_PROP_USB_SS, (union extcon_property_value)(int)mux); @@ -237,6 +354,8 @@ static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info, EXTCON_PROP_DISP_HPD, (union extcon_property_value)(int)hpd); + extcon_sync(info->edev, EXTCON_USB); + extcon_sync(info->edev, EXTCON_USB_HOST); extcon_sync(info->edev, EXTCON_DISP_DP); } else if (hpd) { @@ -322,13 +441,28 @@ static int extcon_cros_ec_probe(struct platform_device *pdev) return ret; } + extcon_set_property_capability(info->edev, EXTCON_USB, + EXTCON_PROP_USB_VBUS); + extcon_set_property_capability(info->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_VBUS); + extcon_set_property_capability(info->edev, EXTCON_USB, + EXTCON_PROP_USB_TYPEC_POLARITY); + extcon_set_property_capability(info->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_TYPEC_POLARITY); extcon_set_property_capability(info->edev, EXTCON_DISP_DP, EXTCON_PROP_USB_TYPEC_POLARITY); + extcon_set_property_capability(info->edev, EXTCON_USB, + EXTCON_PROP_USB_SS); + extcon_set_property_capability(info->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_SS); extcon_set_property_capability(info->edev, EXTCON_DISP_DP, EXTCON_PROP_USB_SS); extcon_set_property_capability(info->edev, EXTCON_DISP_DP, EXTCON_PROP_DISP_HPD); + info->dr = DR_NONE; + info->pr = false; + platform_set_drvdata(pdev, info); /* Get PD events from the EC */ diff --git a/drivers/leds/leds-pm8058.c b/drivers/leds/leds-pm8058.c index a52674327857..8988ba3b2d65 100644 --- a/drivers/leds/leds-pm8058.c +++ b/drivers/leds/leds-pm8058.c @@ -106,7 +106,7 @@ static int pm8058_led_probe(struct platform_device *pdev) if (!led) return -ENOMEM; - led->ledtype = (u32)of_device_get_match_data(&pdev->dev); + led->ledtype = (u32)(unsigned long)of_device_get_match_data(&pdev->dev); map = dev_get_regmap(pdev->dev.parent, NULL); if (!map) { diff --git a/drivers/memstick/host/Kconfig b/drivers/memstick/host/Kconfig index 7310e32b5991..aa2b0786bbe9 100644 --- a/drivers/memstick/host/Kconfig +++ b/drivers/memstick/host/Kconfig @@ -45,7 +45,7 @@ config MEMSTICK_R592 config MEMSTICK_REALTEK_PCI tristate "Realtek PCI-E Memstick Card Interface Driver" - depends on MFD_RTSX_PCI + depends on MISC_RTSX_PCI help Say Y here to include driver code to support Memstick card interface of Realtek PCI-E card reader @@ -55,7 +55,7 @@ config MEMSTICK_REALTEK_PCI config MEMSTICK_REALTEK_USB tristate "Realtek USB Memstick Card Interface Driver" - depends on MFD_RTSX_USB + depends on MISC_RTSX_USB help Say Y here to include driver code to support Memstick card interface of Realtek RTS5129/39 series USB card reader diff --git a/drivers/memstick/host/rtsx_pci_ms.c b/drivers/memstick/host/rtsx_pci_ms.c index 818fa94354ae..a44b4578ba4d 100644 --- a/drivers/memstick/host/rtsx_pci_ms.c +++ b/drivers/memstick/host/rtsx_pci_ms.c @@ -24,7 +24,7 @@ #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/memstick.h> -#include <linux/mfd/rtsx_pci.h> +#include <linux/rtsx_pci.h> #include <asm/unaligned.h> struct realtek_pci_ms { diff --git a/drivers/memstick/host/rtsx_usb_ms.c b/drivers/memstick/host/rtsx_usb_ms.c index 2e3cf012ef48..4f64563df7de 100644 --- a/drivers/memstick/host/rtsx_usb_ms.c +++ b/drivers/memstick/host/rtsx_usb_ms.c @@ -25,7 +25,7 @@ #include <linux/workqueue.h> #include <linux/memstick.h> #include <linux/kthread.h> -#include <linux/mfd/rtsx_usb.h> +#include <linux/rtsx_usb.h> #include <linux/pm_runtime.h> #include <linux/mutex.h> #include <linux/sched.h> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1d20a800e967..b860eb5aa194 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -222,6 +222,16 @@ config MFD_CROS_EC_SPI response time cannot be guaranteed, we support ignoring 'pre-amble' bytes before the response actually starts. +config MFD_CROS_EC_CHARDEV + tristate "Chrome OS Embedded Controller userspace device interface" + depends on MFD_CROS_EC + select CROS_EC_CTL + ---help--- + This driver adds support to talk with the ChromeOS EC from userspace. + + If you have a supported Chromebook, choose Y or M here. + The module will be called cros_ec_dev. + config MFD_ASIC3 bool "Compaq ASIC3" depends on GPIOLIB && ARM @@ -877,7 +887,7 @@ config UCB1400_CORE config MFD_PM8XXX tristate "Qualcomm PM8xxx PMIC chips driver" - depends on (ARM || HEXAGON) + depends on (ARM || HEXAGON || COMPILE_TEST) select IRQ_DOMAIN select MFD_CORE select REGMAP @@ -929,17 +939,6 @@ config MFD_RDC321X southbridge which provides access to GPIOs and Watchdog using the southbridge PCI device configuration space. -config MFD_RTSX_PCI - tristate "Realtek PCI-E card reader" - depends on PCI - select MFD_CORE - help - This supports for Realtek PCI-Express card reader including rts5209, - rts5227, rts522A, rts5229, rts5249, rts524A, rts525A, rtl8411, etc. - Realtek card reader supports access to many types of memory cards, - such as Memory Stick, Memory Stick Pro, Secure Digital and - MultiMediaCard. - config MFD_RT5033 tristate "Richtek RT5033 Power Management IC" depends on I2C @@ -953,16 +952,6 @@ config MFD_RT5033 sub-devices like charger, fuel gauge, flash LED, current source, LDO and Buck. -config MFD_RTSX_USB - tristate "Realtek USB card reader" - depends on USB - select MFD_CORE - help - Select this option to get support for Realtek USB 2.0 card readers - including RTS5129, RTS5139, RTS5179 and RTS5170. - Realtek card reader supports access to many types of memory cards, - such as Memory Stick Pro, Secure Digital and MultiMediaCard. - config MFD_RC5T583 bool "Ricoh RC5T583 Power Management system device" depends on I2C=y @@ -1859,5 +1848,13 @@ config MFD_VEXPRESS_SYSREG System Registers are the platform configuration block on the ARM Ltd. Versatile Express board. +config RAVE_SP_CORE + tristate "RAVE SP MCU core driver" + depends on SERIAL_DEV_BUS + select CRC_CCITT + help + Select this to get support for the Supervisory Processor + device found on several devices in RAVE line of hardware. + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index d9474ade32e6..d9d2cf0d32ef 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -17,12 +17,9 @@ cros_ec_core-$(CONFIG_ACPI) += cros_ec_acpi_gpe.o obj-$(CONFIG_MFD_CROS_EC) += cros_ec_core.o obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o +obj-$(CONFIG_MFD_CROS_EC_CHARDEV) += cros_ec_dev.o obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o -rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o -obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o -obj-$(CONFIG_MFD_RTSX_USB) += rtsx_usb.o - obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o @@ -230,3 +227,5 @@ obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o obj-$(CONFIG_MFD_SC27XX_PMIC) += sprd-sc27xx-spi.o +obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o + diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index c1c815241e02..1afa27de7191 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -1258,6 +1258,19 @@ static struct ab8500_prcmu_ranges ab8540_debug_ranges[AB8500_NUM_BANKS] = { }, }; +#define DEFINE_SHOW_ATTRIBUTE(__name) \ +static int __name ## _open(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, __name ## _show, inode->i_private); \ +} \ + \ +static const struct file_operations __name ## _fops = { \ + .owner = THIS_MODULE, \ + .open = __name ## _open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ +} \ static irqreturn_t ab8500_debug_handler(int irq, void *data) { @@ -1318,7 +1331,7 @@ static int ab8500_registers_print(struct device *dev, u32 bank, return 0; } -static int ab8500_print_bank_registers(struct seq_file *s, void *p) +static int ab8500_bank_registers_show(struct seq_file *s, void *p) { struct device *dev = s->private; u32 bank = debug_bank; @@ -1330,18 +1343,7 @@ static int ab8500_print_bank_registers(struct seq_file *s, void *p) return ab8500_registers_print(dev, bank, s); } -static int ab8500_registers_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_print_bank_registers, inode->i_private); -} - -static const struct file_operations ab8500_registers_fops = { - .open = ab8500_registers_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(ab8500_bank_registers); static int ab8500_print_all_banks(struct seq_file *s, void *p) { @@ -1528,7 +1530,7 @@ void ab8500_debug_register_interrupt(int line) num_interrupts[line]++; } -static int ab8500_interrupts_print(struct seq_file *s, void *p) +static int ab8500_interrupts_show(struct seq_file *s, void *p) { int line; @@ -1557,10 +1559,7 @@ static int ab8500_interrupts_print(struct seq_file *s, void *p) return 0; } -static int ab8500_interrupts_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_interrupts_print, inode->i_private); -} +DEFINE_SHOW_ATTRIBUTE(ab8500_interrupts); /* * - HWREG DB8500 formated routines @@ -1603,7 +1602,7 @@ static int ab8500_hwreg_open(struct inode *inode, struct file *file) #define AB8500_LAST_SIM_REG 0x8B #define AB8505_LAST_SIM_REG 0x8C -static int ab8500_print_modem_registers(struct seq_file *s, void *p) +static int ab8500_modem_show(struct seq_file *s, void *p) { struct device *dev = s->private; struct ab8500 *ab8500; @@ -1620,18 +1619,15 @@ static int ab8500_print_modem_registers(struct seq_file *s, void *p) err = abx500_get_register_interruptible(dev, AB8500_REGU_CTRL1, AB8500_SUPPLY_CONTROL_REG, &orig_value); - if (err < 0) { - dev_err(dev, "ab->read fail %d\n", err); - return err; - } + if (err < 0) + goto report_read_failure; + /* Config 1 will allow APE side to read SIM registers */ err = abx500_set_register_interruptible(dev, AB8500_REGU_CTRL1, AB8500_SUPPLY_CONTROL_REG, AB8500_SUPPLY_CONTROL_CONFIG_1); - if (err < 0) { - dev_err(dev, "ab->write fail %d\n", err); - return err; - } + if (err < 0) + goto report_write_failure; seq_printf(s, " bank 0x%02X:\n", bank); @@ -1641,36 +1637,30 @@ static int ab8500_print_modem_registers(struct seq_file *s, void *p) for (reg = AB8500_FIRST_SIM_REG; reg <= last_sim_reg; reg++) { err = abx500_get_register_interruptible(dev, bank, reg, &value); - if (err < 0) { - dev_err(dev, "ab->read fail %d\n", err); - return err; - } + if (err < 0) + goto report_read_failure; + seq_printf(s, " [0x%02X/0x%02X]: 0x%02X\n", bank, reg, value); } err = abx500_set_register_interruptible(dev, AB8500_REGU_CTRL1, AB8500_SUPPLY_CONTROL_REG, orig_value); - if (err < 0) { - dev_err(dev, "ab->write fail %d\n", err); - return err; - } + if (err < 0) + goto report_write_failure; + return 0; -} -static int ab8500_modem_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_print_modem_registers, - inode->i_private); +report_read_failure: + dev_err(dev, "ab->read fail %d\n", err); + return err; + +report_write_failure: + dev_err(dev, "ab->write fail %d\n", err); + return err; } -static const struct file_operations ab8500_modem_fops = { - .open = ab8500_modem_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(ab8500_modem); -static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p) +static int ab8500_gpadc_bat_ctrl_show(struct seq_file *s, void *p) { int bat_ctrl_raw; int bat_ctrl_convert; @@ -1687,21 +1677,9 @@ static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p) return 0; } -static int ab8500_gpadc_bat_ctrl_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_gpadc_bat_ctrl_print, - inode->i_private); -} - -static const struct file_operations ab8500_gpadc_bat_ctrl_fops = { - .open = ab8500_gpadc_bat_ctrl_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_bat_ctrl); -static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p) +static int ab8500_gpadc_btemp_ball_show(struct seq_file *s, void *p) { int btemp_ball_raw; int btemp_ball_convert; @@ -1718,22 +1696,9 @@ static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p) return 0; } -static int ab8500_gpadc_btemp_ball_open(struct inode *inode, - struct file *file) -{ - return single_open(file, ab8500_gpadc_btemp_ball_print, - inode->i_private); -} +DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_btemp_ball); -static const struct file_operations ab8500_gpadc_btemp_ball_fops = { - .open = ab8500_gpadc_btemp_ball_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8500_gpadc_main_charger_v_print(struct seq_file *s, void *p) +static int ab8500_gpadc_main_charger_v_show(struct seq_file *s, void *p) { int main_charger_v_raw; int main_charger_v_convert; @@ -1750,22 +1715,9 @@ static int ab8500_gpadc_main_charger_v_print(struct seq_file *s, void *p) return 0; } -static int ab8500_gpadc_main_charger_v_open(struct inode *inode, - struct file *file) -{ - return single_open(file, ab8500_gpadc_main_charger_v_print, - inode->i_private); -} - -static const struct file_operations ab8500_gpadc_main_charger_v_fops = { - .open = ab8500_gpadc_main_charger_v_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_main_charger_v); -static int ab8500_gpadc_acc_detect1_print(struct seq_file *s, void *p) +static int ab8500_gpadc_acc_detect1_show(struct seq_file *s, void *p) { int acc_detect1_raw; int acc_detect1_convert; @@ -1782,22 +1734,9 @@ static int ab8500_gpadc_acc_detect1_print(struct seq_file *s, void *p) return 0; } -static int ab8500_gpadc_acc_detect1_open(struct inode *inode, - struct file *file) -{ - return single_open(file, ab8500_gpadc_acc_detect1_print, - inode->i_private); -} - -static const struct file_operations ab8500_gpadc_acc_detect1_fops = { - .open = ab8500_gpadc_acc_detect1_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_acc_detect1); -static int ab8500_gpadc_acc_detect2_print(struct seq_file *s, void *p) +static int ab8500_gpadc_acc_detect2_show(struct seq_file *s, void *p) { int acc_detect2_raw; int acc_detect2_convert; @@ -1814,22 +1753,9 @@ static int ab8500_gpadc_acc_detect2_print(struct seq_file *s, void *p) return 0; } -static int ab8500_gpadc_acc_detect2_open(struct inode *inode, - struct file *file) -{ - return single_open(file, ab8500_gpadc_acc_detect2_print, - inode->i_private); -} - -static const struct file_operations ab8500_gpadc_acc_detect2_fops = { - .open = ab8500_gpadc_acc_detect2_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_acc_detect2); -static int ab8500_gpadc_aux1_print(struct seq_file *s, void *p) +static int ab8500_gpadc_aux1_show(struct seq_file *s, void *p) { int aux1_raw; int aux1_convert; @@ -1846,20 +1772,9 @@ static int ab8500_gpadc_aux1_print(struct seq_file *s, void *p) return 0; } -static int ab8500_gpadc_aux1_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_gpadc_aux1_print, inode->i_private); -} +DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_aux1); -static const struct file_operations ab8500_gpadc_aux1_fops = { - .open = ab8500_gpadc_aux1_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8500_gpadc_aux2_print(struct seq_file *s, void *p) +static int ab8500_gpadc_aux2_show(struct seq_file *s, void *p) { int aux2_raw; int aux2_convert; @@ -1876,20 +1791,9 @@ static int ab8500_gpadc_aux2_print(struct seq_file *s, void *p) return 0; } -static int ab8500_gpadc_aux2_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_gpadc_aux2_print, inode->i_private); -} - -static const struct file_operations ab8500_gpadc_aux2_fops = { - .open = ab8500_gpadc_aux2_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_aux2); -static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p) +static int ab8500_gpadc_main_bat_v_show(struct seq_file *s, void *p) { int main_bat_v_raw; int main_bat_v_convert; @@ -1906,22 +1810,9 @@ static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p) return 0; } -static int ab8500_gpadc_main_bat_v_open(struct inode *inode, - struct file *file) -{ - return single_open(file, ab8500_gpadc_main_bat_v_print, - inode->i_private); -} +DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_main_bat_v); -static const struct file_operations ab8500_gpadc_main_bat_v_fops = { - .open = ab8500_gpadc_main_bat_v_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8500_gpadc_vbus_v_print(struct seq_file *s, void *p) +static int ab8500_gpadc_vbus_v_show(struct seq_file *s, void *p) { int vbus_v_raw; int vbus_v_convert; @@ -1938,20 +1829,9 @@ static int ab8500_gpadc_vbus_v_print(struct seq_file *s, void *p) return 0; } -static int ab8500_gpadc_vbus_v_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_gpadc_vbus_v_print, inode->i_private); -} +DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_vbus_v); -static const struct file_operations ab8500_gpadc_vbus_v_fops = { - .open = ab8500_gpadc_vbus_v_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8500_gpadc_main_charger_c_print(struct seq_file *s, void *p) +static int ab8500_gpadc_main_charger_c_show(struct seq_file *s, void *p) { int main_charger_c_raw; int main_charger_c_convert; @@ -1968,22 +1848,9 @@ static int ab8500_gpadc_main_charger_c_print(struct seq_file *s, void *p) return 0; } -static int ab8500_gpadc_main_charger_c_open(struct inode *inode, - struct file *file) -{ - return single_open(file, ab8500_gpadc_main_charger_c_print, - inode->i_private); -} - -static const struct file_operations ab8500_gpadc_main_charger_c_fops = { - .open = ab8500_gpadc_main_charger_c_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_main_charger_c); -static int ab8500_gpadc_usb_charger_c_print(struct seq_file *s, void *p) +static int ab8500_gpadc_usb_charger_c_show(struct seq_file *s, void *p) { int usb_charger_c_raw; int usb_charger_c_convert; @@ -2000,22 +1867,9 @@ static int ab8500_gpadc_usb_charger_c_print(struct seq_file *s, void *p) return 0; } -static int ab8500_gpadc_usb_charger_c_open(struct inode *inode, - struct file *file) -{ - return single_open(file, ab8500_gpadc_usb_charger_c_print, - inode->i_private); -} - -static const struct file_operations ab8500_gpadc_usb_charger_c_fops = { - .open = ab8500_gpadc_usb_charger_c_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_usb_charger_c); -static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p) +static int ab8500_gpadc_bk_bat_v_show(struct seq_file *s, void *p) { int bk_bat_v_raw; int bk_bat_v_convert; @@ -2032,21 +1886,9 @@ static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p) return 0; } -static int ab8500_gpadc_bk_bat_v_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_gpadc_bk_bat_v_print, - inode->i_private); -} - -static const struct file_operations ab8500_gpadc_bk_bat_v_fops = { - .open = ab8500_gpadc_bk_bat_v_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_bk_bat_v); -static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p) +static int ab8500_gpadc_die_temp_show(struct seq_file *s, void *p) { int die_temp_raw; int die_temp_convert; @@ -2063,21 +1905,9 @@ static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p) return 0; } -static int ab8500_gpadc_die_temp_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_gpadc_die_temp_print, - inode->i_private); -} +DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_die_temp); -static const struct file_operations ab8500_gpadc_die_temp_fops = { - .open = ab8500_gpadc_die_temp_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8500_gpadc_usb_id_print(struct seq_file *s, void *p) +static int ab8500_gpadc_usb_id_show(struct seq_file *s, void *p) { int usb_id_raw; int usb_id_convert; @@ -2094,20 +1924,9 @@ static int ab8500_gpadc_usb_id_print(struct seq_file *s, void *p) return 0; } -static int ab8500_gpadc_usb_id_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8500_gpadc_usb_id_print, inode->i_private); -} - -static const struct file_operations ab8500_gpadc_usb_id_fops = { - .open = ab8500_gpadc_usb_id_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(ab8500_gpadc_usb_id); -static int ab8540_gpadc_xtal_temp_print(struct seq_file *s, void *p) +static int ab8540_gpadc_xtal_temp_show(struct seq_file *s, void *p) { int xtal_temp_raw; int xtal_temp_convert; @@ -2124,21 +1943,9 @@ static int ab8540_gpadc_xtal_temp_print(struct seq_file *s, void *p) return 0; } -static int ab8540_gpadc_xtal_temp_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8540_gpadc_xtal_temp_print, - inode->i_private); -} +DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_xtal_temp); -static const struct file_operations ab8540_gpadc_xtal_temp_fops = { - .open = ab8540_gpadc_xtal_temp_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8540_gpadc_vbat_true_meas_print(struct seq_file *s, void *p) +static int ab8540_gpadc_vbat_true_meas_show(struct seq_file *s, void *p) { int vbat_true_meas_raw; int vbat_true_meas_convert; @@ -2156,22 +1963,9 @@ static int ab8540_gpadc_vbat_true_meas_print(struct seq_file *s, void *p) return 0; } -static int ab8540_gpadc_vbat_true_meas_open(struct inode *inode, - struct file *file) -{ - return single_open(file, ab8540_gpadc_vbat_true_meas_print, - inode->i_private); -} +DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_vbat_true_meas); -static const struct file_operations ab8540_gpadc_vbat_true_meas_fops = { - .open = ab8540_gpadc_vbat_true_meas_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8540_gpadc_bat_ctrl_and_ibat_print(struct seq_file *s, void *p) +static int ab8540_gpadc_bat_ctrl_and_ibat_show(struct seq_file *s, void *p) { int bat_ctrl_raw; int bat_ctrl_convert; @@ -2197,22 +1991,9 @@ static int ab8540_gpadc_bat_ctrl_and_ibat_print(struct seq_file *s, void *p) return 0; } -static int ab8540_gpadc_bat_ctrl_and_ibat_open(struct inode *inode, - struct file *file) -{ - return single_open(file, ab8540_gpadc_bat_ctrl_and_ibat_print, - inode->i_private); -} - -static const struct file_operations ab8540_gpadc_bat_ctrl_and_ibat_fops = { - .open = ab8540_gpadc_bat_ctrl_and_ibat_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_bat_ctrl_and_ibat); -static int ab8540_gpadc_vbat_meas_and_ibat_print(struct seq_file *s, void *p) +static int ab8540_gpadc_vbat_meas_and_ibat_show(struct seq_file *s, void *p) { int vbat_meas_raw; int vbat_meas_convert; @@ -2237,23 +2018,9 @@ static int ab8540_gpadc_vbat_meas_and_ibat_print(struct seq_file *s, void *p) return 0; } -static int ab8540_gpadc_vbat_meas_and_ibat_open(struct inode *inode, - struct file *file) -{ - return single_open(file, ab8540_gpadc_vbat_meas_and_ibat_print, - inode->i_private); -} +DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_vbat_meas_and_ibat); -static const struct file_operations ab8540_gpadc_vbat_meas_and_ibat_fops = { - .open = ab8540_gpadc_vbat_meas_and_ibat_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab8540_gpadc_vbat_true_meas_and_ibat_print(struct seq_file *s, - void *p) +static int ab8540_gpadc_vbat_true_meas_and_ibat_show(struct seq_file *s, void *p) { int vbat_true_meas_raw; int vbat_true_meas_convert; @@ -2279,23 +2046,9 @@ static int ab8540_gpadc_vbat_true_meas_and_ibat_print(struct seq_file *s, return 0; } -static int ab8540_gpadc_vbat_true_meas_and_ibat_open(struct inode *inode, - struct file *file) -{ - return single_open(file, ab8540_gpadc_vbat_true_meas_and_ibat_print, - inode->i_private); -} - -static const struct file_operations -ab8540_gpadc_vbat_true_meas_and_ibat_fops = { - .open = ab8540_gpadc_vbat_true_meas_and_ibat_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_vbat_true_meas_and_ibat); -static int ab8540_gpadc_bat_temp_and_ibat_print(struct seq_file *s, void *p) +static int ab8540_gpadc_bat_temp_and_ibat_show(struct seq_file *s, void *p) { int bat_temp_raw; int bat_temp_convert; @@ -2320,22 +2073,9 @@ static int ab8540_gpadc_bat_temp_and_ibat_print(struct seq_file *s, void *p) return 0; } -static int ab8540_gpadc_bat_temp_and_ibat_open(struct inode *inode, - struct file *file) -{ - return single_open(file, ab8540_gpadc_bat_temp_and_ibat_print, - inode->i_private); -} - -static const struct file_operations ab8540_gpadc_bat_temp_and_ibat_fops = { - .open = ab8540_gpadc_bat_temp_and_ibat_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_bat_temp_and_ibat); -static int ab8540_gpadc_otp_cal_print(struct seq_file *s, void *p) +static int ab8540_gpadc_otp_calib_show(struct seq_file *s, void *p) { struct ab8500_gpadc *gpadc; u16 vmain_l, vmain_h, btemp_l, btemp_h; @@ -2359,18 +2099,7 @@ static int ab8540_gpadc_otp_cal_print(struct seq_file *s, void *p) return 0; } -static int ab8540_gpadc_otp_cal_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab8540_gpadc_otp_cal_print, inode->i_private); -} - -static const struct file_operations ab8540_gpadc_otp_calib_fops = { - .open = ab8540_gpadc_otp_cal_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(ab8540_gpadc_otp_calib); static int ab8500_gpadc_avg_sample_print(struct seq_file *s, void *p) { @@ -2903,14 +2632,6 @@ static const struct file_operations ab8500_val_fops = { .owner = THIS_MODULE, }; -static const struct file_operations ab8500_interrupts_fops = { - .open = ab8500_interrupts_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - static const struct file_operations ab8500_subscribe_fops = { .open = ab8500_subscribe_unsubscribe_open, .write = ab8500_subscribe_write, @@ -2997,7 +2718,7 @@ static int ab8500_debug_probe(struct platform_device *plf) goto err; file = debugfs_create_file("all-bank-registers", S_IRUGO, ab8500_dir, - &plf->dev, &ab8500_registers_fops); + &plf->dev, &ab8500_bank_registers_fops); if (!file) goto err; diff --git a/drivers/mfd/atmel-flexcom.c b/drivers/mfd/atmel-flexcom.c index 064bde9cff5a..f684a93a3340 100644 --- a/drivers/mfd/atmel-flexcom.c +++ b/drivers/mfd/atmel-flexcom.c @@ -39,34 +39,43 @@ #define FLEX_MR_OPMODE(opmode) (((opmode) << FLEX_MR_OPMODE_OFFSET) & \ FLEX_MR_OPMODE_MASK) +struct atmel_flexcom { + void __iomem *base; + u32 opmode; + struct clk *clk; +}; static int atmel_flexcom_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - struct clk *clk; struct resource *res; - void __iomem *base; - u32 opmode; + struct atmel_flexcom *ddata; int err; - err = of_property_read_u32(np, "atmel,flexcom-mode", &opmode); + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + platform_set_drvdata(pdev, ddata); + + err = of_property_read_u32(np, "atmel,flexcom-mode", &ddata->opmode); if (err) return err; - if (opmode < ATMEL_FLEXCOM_MODE_USART || - opmode > ATMEL_FLEXCOM_MODE_TWI) + if (ddata->opmode < ATMEL_FLEXCOM_MODE_USART || + ddata->opmode > ATMEL_FLEXCOM_MODE_TWI) return -EINVAL; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); + ddata->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ddata->base)) + return PTR_ERR(ddata->base); - clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) - return PTR_ERR(clk); + ddata->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(ddata->clk)) + return PTR_ERR(ddata->clk); - err = clk_prepare_enable(clk); + err = clk_prepare_enable(ddata->clk); if (err) return err; @@ -76,9 +85,9 @@ static int atmel_flexcom_probe(struct platform_device *pdev) * inaccessible and are read as zero. Also the external I/O lines of the * Flexcom are muxed to reach the selected device. */ - writel(FLEX_MR_OPMODE(opmode), base + FLEX_MR); + writel(FLEX_MR_OPMODE(ddata->opmode), ddata->base + FLEX_MR); - clk_disable_unprepare(clk); + clk_disable_unprepare(ddata->clk); return devm_of_platform_populate(&pdev->dev); } @@ -89,10 +98,34 @@ static const struct of_device_id atmel_flexcom_of_match[] = { }; MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match); +#ifdef CONFIG_PM_SLEEP +static int atmel_flexcom_resume(struct device *dev) +{ + struct atmel_flexcom *ddata = dev_get_drvdata(dev); + int err; + u32 val; + + err = clk_prepare_enable(ddata->clk); + if (err) + return err; + + val = FLEX_MR_OPMODE(ddata->opmode), + writel(val, ddata->base + FLEX_MR); + + clk_disable_unprepare(ddata->clk); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(atmel_flexcom_pm_ops, NULL, + atmel_flexcom_resume); + static struct platform_driver atmel_flexcom_driver = { .probe = atmel_flexcom_probe, .driver = { .name = "atmel_flexcom", + .pm = &atmel_flexcom_pm_ops, .of_match_table = atmel_flexcom_of_match, }, }; diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 2468b431bb22..e94c72c2faa2 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -129,6 +129,7 @@ static const struct regmap_range axp288_volatile_ranges[] = { regmap_reg_range(AXP20X_PWR_INPUT_STATUS, AXP288_POWER_REASON), regmap_reg_range(AXP288_BC_GLOBAL, AXP288_BC_GLOBAL), regmap_reg_range(AXP288_BC_DET_STAT, AXP288_BC_DET_STAT), + regmap_reg_range(AXP20X_CHRG_BAK_CTRL, AXP20X_CHRG_BAK_CTRL), regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IPSOUT_V_HIGH_L), regmap_reg_range(AXP20X_TIMER_CTRL, AXP20X_TIMER_CTRL), regmap_reg_range(AXP22X_GPIO_STATE, AXP22X_GPIO_STATE), @@ -878,6 +879,9 @@ static struct mfd_cell axp813_cells[] = { .resources = axp803_pek_resources, }, { .name = "axp20x-regulator", + }, { + .name = "axp20x-gpio", + .of_compatible = "x-powers,axp813-gpio", } }; diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index b0ca5a4c841e..d61024141e2b 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -40,13 +40,13 @@ static struct cros_ec_platform pd_p = { }; static const struct mfd_cell ec_cell = { - .name = "cros-ec-ctl", + .name = "cros-ec-dev", .platform_data = &ec_p, .pdata_size = sizeof(ec_p), }; static const struct mfd_cell ec_pd_cell = { - .name = "cros-ec-ctl", + .name = "cros-ec-dev", .platform_data = &pd_p, .pdata_size = sizeof(pd_p), }; diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index cf6c4f0846b8..e4fafdd96e5e 100644 --- a/drivers/platform/chrome/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -25,9 +25,10 @@ #include <linux/slab.h> #include <linux/uaccess.h> -#include "cros_ec_debugfs.h" #include "cros_ec_dev.h" +#define DRV_NAME "cros-ec-dev" + /* Device variables */ #define CROS_MAX_DEV 128 static int ec_major; @@ -461,7 +462,7 @@ static int ec_device_remove(struct platform_device *pdev) } static const struct platform_device_id cros_ec_id[] = { - { "cros-ec-ctl", 0 }, + { DRV_NAME, 0 }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(platform, cros_ec_id); @@ -493,7 +494,7 @@ static const struct dev_pm_ops cros_ec_dev_pm_ops = { static struct platform_driver cros_ec_dev_driver = { .driver = { - .name = "cros-ec-ctl", + .name = DRV_NAME, .pm = &cros_ec_dev_pm_ops, }, .probe = ec_device_probe, @@ -544,6 +545,7 @@ static void __exit cros_ec_dev_exit(void) module_init(cros_ec_dev_init); module_exit(cros_ec_dev_exit); +MODULE_ALIAS("platform:" DRV_NAME); MODULE_AUTHOR("Bill Richardson <wfrichar@chromium.org>"); MODULE_DESCRIPTION("Userspace interface to the Chrome OS Embedded Controller"); MODULE_VERSION("1.0"); diff --git a/drivers/platform/chrome/cros_ec_dev.h b/drivers/mfd/cros_ec_dev.h index 45e9453608c5..45e9453608c5 100644 --- a/drivers/platform/chrome/cros_ec_dev.h +++ b/drivers/mfd/cros_ec_dev.h diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index 59c82cdcf48d..1b52b8557034 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -72,8 +72,7 @@ * struct cros_ec_spi - information about a SPI-connected EC * * @spi: SPI device we are connected to - * @last_transfer_ns: time that we last finished a transfer, or 0 if there - * if no record + * @last_transfer_ns: time that we last finished a transfer. * @start_of_msg_delay: used to set the delay_usecs on the spi_transfer that * is sent when we want to turn on CS at the start of a transaction. * @end_of_msg_delay: used to set the delay_usecs on the spi_transfer that @@ -379,18 +378,15 @@ static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev, u8 sum; u8 rx_byte; int ret = 0, final_ret; + unsigned long delay; len = cros_ec_prepare_tx(ec_dev, ec_msg); dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); /* If it's too soon to do another transaction, wait */ - if (ec_spi->last_transfer_ns) { - unsigned long delay; /* The delay completed so far */ - - delay = ktime_get_ns() - ec_spi->last_transfer_ns; - if (delay < EC_SPI_RECOVERY_TIME_NS) - ndelay(EC_SPI_RECOVERY_TIME_NS - delay); - } + delay = ktime_get_ns() - ec_spi->last_transfer_ns; + if (delay < EC_SPI_RECOVERY_TIME_NS) + ndelay(EC_SPI_RECOVERY_TIME_NS - delay); rx_buf = kzalloc(len, GFP_KERNEL); if (!rx_buf) @@ -509,18 +505,15 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev, u8 rx_byte; int sum; int ret = 0, final_ret; + unsigned long delay; len = cros_ec_prepare_tx(ec_dev, ec_msg); dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); /* If it's too soon to do another transaction, wait */ - if (ec_spi->last_transfer_ns) { - unsigned long delay; /* The delay completed so far */ - - delay = ktime_get_ns() - ec_spi->last_transfer_ns; - if (delay < EC_SPI_RECOVERY_TIME_NS) - ndelay(EC_SPI_RECOVERY_TIME_NS - delay); - } + delay = ktime_get_ns() - ec_spi->last_transfer_ns; + if (delay < EC_SPI_RECOVERY_TIME_NS) + ndelay(EC_SPI_RECOVERY_TIME_NS - delay); rx_buf = kzalloc(len, GFP_KERNEL); if (!rx_buf) diff --git a/drivers/mfd/intel_soc_pmic_core.c b/drivers/mfd/intel_soc_pmic_core.c index 36adf9e8153e..274306d98ac1 100644 --- a/drivers/mfd/intel_soc_pmic_core.c +++ b/drivers/mfd/intel_soc_pmic_core.c @@ -16,7 +16,6 @@ * Author: Zhu, Lejun <lejun.zhu@linux.intel.com> */ -#include <linux/acpi.h> #include <linux/module.h> #include <linux/mfd/core.h> #include <linux/i2c.h> diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c index 55d824b3a808..390b27cb2c2e 100644 --- a/drivers/mfd/kempld-core.c +++ b/drivers/mfd/kempld-core.c @@ -458,7 +458,7 @@ static int kempld_probe(struct platform_device *pdev) return -EINVAL; pld->io_base = devm_ioport_map(dev, ioport->start, - ioport->end - ioport->start); + resource_size(ioport)); if (!pld->io_base) return -ENOMEM; diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index cf1120abbf52..53dc1a43472c 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -1143,11 +1143,6 @@ static int lpc_ich_init_spi(struct pci_dev *dev) res->end = res->start + SPIBASE_APL_SZ - 1; pci_bus_read_config_dword(bus, spi, BCR, &bcr); - if (!(bcr & BCR_WPD)) { - bcr |= BCR_WPD; - pci_bus_write_config_dword(bus, spi, BCR, bcr); - pci_bus_read_config_dword(bus, spi, BCR, &bcr); - } info->writeable = !!(bcr & BCR_WPD); } diff --git a/drivers/mfd/max77843.c b/drivers/mfd/max77843.c index dc5caeaaa6a1..da9612dbb222 100644 --- a/drivers/mfd/max77843.c +++ b/drivers/mfd/max77843.c @@ -15,7 +15,6 @@ #include <linux/i2c.h> #include <linux/init.h> #include <linux/interrupt.h> -#include <linux/init.h> #include <linux/mfd/core.h> #include <linux/mfd/max77693-common.h> #include <linux/mfd/max77843-private.h> diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c index 3922a93f9f92..663a2398b6b1 100644 --- a/drivers/mfd/palmas.c +++ b/drivers/mfd/palmas.c @@ -430,6 +430,7 @@ static void palmas_power_off(void) { unsigned int addr; int ret, slave; + u8 powerhold_mask; struct device_node *np = palmas_dev->dev->of_node; if (of_property_read_bool(np, "ti,palmas-override-powerhold")) { @@ -437,8 +438,15 @@ static void palmas_power_off(void) PALMAS_PRIMARY_SECONDARY_PAD2); slave = PALMAS_BASE_TO_SLAVE(PALMAS_PU_PD_OD_BASE); + if (of_device_is_compatible(np, "ti,tps65917")) + powerhold_mask = + TPS65917_PRIMARY_SECONDARY_PAD2_GPIO_5_MASK; + else + powerhold_mask = + PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_7_MASK; + ret = regmap_update_bits(palmas_dev->regmap[slave], addr, - PALMAS_PRIMARY_SECONDARY_PAD2_GPIO_7_MASK, 0); + powerhold_mask, 0); if (ret) dev_err(palmas_dev->dev, "Unable to write PRIMARY_SECONDARY_PAD2 %d\n", diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index 6155d123a84e..f952dff6765f 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -149,7 +149,7 @@ pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name, *pdev = platform_device_alloc(name, -1); if (!*pdev) { - dev_err(pcf->dev, "Falied to allocate %s\n", name); + dev_err(pcf->dev, "Failed to allocate %s\n", name); return; } diff --git a/drivers/mfd/rave-sp.c b/drivers/mfd/rave-sp.c new file mode 100644 index 000000000000..5c858e784a89 --- /dev/null +++ b/drivers/mfd/rave-sp.c @@ -0,0 +1,710 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Multifunction core driver for Zodiac Inflight Innovations RAVE + * Supervisory Processor(SP) MCU that is connected via dedicated UART + * port + * + * Copyright (C) 2017 Zodiac Inflight Innovations + */ + +#include <linux/atomic.h> +#include <linux/crc-ccitt.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/mfd/rave-sp.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/sched.h> +#include <linux/serdev.h> +#include <asm/unaligned.h> + +/* + * UART protocol using following entities: + * - message to MCU => ACK response + * - event from MCU => event ACK + * + * Frame structure: + * <STX> <DATA> <CHECKSUM> <ETX> + * Where: + * - STX - is start of transmission character + * - ETX - end of transmission + * - DATA - payload + * - CHECKSUM - checksum calculated on <DATA> + * + * If <DATA> or <CHECKSUM> contain one of control characters, then it is + * escaped using <DLE> control code. Added <DLE> does not participate in + * checksum calculation. + */ +#define RAVE_SP_STX 0x02 +#define RAVE_SP_ETX 0x03 +#define RAVE_SP_DLE 0x10 + +#define RAVE_SP_MAX_DATA_SIZE 64 +#define RAVE_SP_CHECKSUM_SIZE 2 /* Worst case scenario on RDU2 */ +/* + * We don't store STX, ETX and unescaped bytes, so Rx is only + * DATA + CSUM + */ +#define RAVE_SP_RX_BUFFER_SIZE \ + (RAVE_SP_MAX_DATA_SIZE + RAVE_SP_CHECKSUM_SIZE) + +#define RAVE_SP_STX_ETX_SIZE 2 +/* + * For Tx we have to have space for everything, STX, EXT and + * potentially stuffed DATA + CSUM data + csum + */ +#define RAVE_SP_TX_BUFFER_SIZE \ + (RAVE_SP_STX_ETX_SIZE + 2 * RAVE_SP_RX_BUFFER_SIZE) + +#define RAVE_SP_BOOT_SOURCE_GET 0 +#define RAVE_SP_BOOT_SOURCE_SET 1 + +#define RAVE_SP_RDU2_BOARD_TYPE_RMB 0 +#define RAVE_SP_RDU2_BOARD_TYPE_DEB 1 + +#define RAVE_SP_BOOT_SOURCE_SD 0 +#define RAVE_SP_BOOT_SOURCE_EMMC 1 +#define RAVE_SP_BOOT_SOURCE_NOR 2 + +/** + * enum rave_sp_deframer_state - Possible state for de-framer + * + * @RAVE_SP_EXPECT_SOF: Scanning input for start-of-frame marker + * @RAVE_SP_EXPECT_DATA: Got start of frame marker, collecting frame + * @RAVE_SP_EXPECT_ESCAPED_DATA: Got escape character, collecting escaped byte + */ +enum rave_sp_deframer_state { + RAVE_SP_EXPECT_SOF, + RAVE_SP_EXPECT_DATA, + RAVE_SP_EXPECT_ESCAPED_DATA, +}; + +/** + * struct rave_sp_deframer - Device protocol deframer + * + * @state: Current state of the deframer + * @data: Buffer used to collect deframed data + * @length: Number of bytes de-framed so far + */ +struct rave_sp_deframer { + enum rave_sp_deframer_state state; + unsigned char data[RAVE_SP_RX_BUFFER_SIZE]; + size_t length; +}; + +/** + * struct rave_sp_reply - Reply as per RAVE device protocol + * + * @length: Expected reply length + * @data: Buffer to store reply payload in + * @code: Expected reply code + * @ackid: Expected reply ACK ID + * @completion: Successful reply reception completion + */ +struct rave_sp_reply { + size_t length; + void *data; + u8 code; + u8 ackid; + struct completion received; +}; + +/** + * struct rave_sp_checksum - Variant specific checksum implementation details + * + * @length: Caculated checksum length + * @subroutine: Utilized checksum algorithm implementation + */ +struct rave_sp_checksum { + size_t length; + void (*subroutine)(const u8 *, size_t, u8 *); +}; + +/** + * struct rave_sp_variant_cmds - Variant specific command routines + * + * @translate: Generic to variant specific command mapping routine + * + */ +struct rave_sp_variant_cmds { + int (*translate)(enum rave_sp_command); +}; + +/** + * struct rave_sp_variant - RAVE supervisory processor core variant + * + * @checksum: Variant specific checksum implementation + * @cmd: Variant specific command pointer table + * + */ +struct rave_sp_variant { + const struct rave_sp_checksum *checksum; + struct rave_sp_variant_cmds cmd; +}; + +/** + * struct rave_sp - RAVE supervisory processor core + * + * @serdev: Pointer to underlying serdev + * @deframer: Stored state of the protocol deframer + * @ackid: ACK ID used in last reply sent to the device + * @bus_lock: Lock to serialize access to the device + * @reply_lock: Lock protecting @reply + * @reply: Pointer to memory to store reply payload + * + * @variant: Device variant specific information + * @event_notifier_list: Input event notification chain + * + */ +struct rave_sp { + struct serdev_device *serdev; + struct rave_sp_deframer deframer; + atomic_t ackid; + struct mutex bus_lock; + struct mutex reply_lock; + struct rave_sp_reply *reply; + + const struct rave_sp_variant *variant; + struct blocking_notifier_head event_notifier_list; +}; + +static bool rave_sp_id_is_event(u8 code) +{ + return (code & 0xF0) == RAVE_SP_EVNT_BASE; +} + +static void rave_sp_unregister_event_notifier(struct device *dev, void *res) +{ + struct rave_sp *sp = dev_get_drvdata(dev->parent); + struct notifier_block *nb = *(struct notifier_block **)res; + struct blocking_notifier_head *bnh = &sp->event_notifier_list; + + WARN_ON(blocking_notifier_chain_unregister(bnh, nb)); +} + +int devm_rave_sp_register_event_notifier(struct device *dev, + struct notifier_block *nb) +{ + struct rave_sp *sp = dev_get_drvdata(dev->parent); + struct notifier_block **rcnb; + int ret; + + rcnb = devres_alloc(rave_sp_unregister_event_notifier, + sizeof(*rcnb), GFP_KERNEL); + if (!rcnb) + return -ENOMEM; + + ret = blocking_notifier_chain_register(&sp->event_notifier_list, nb); + if (!ret) { + *rcnb = nb; + devres_add(dev, rcnb); + } else { + devres_free(rcnb); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_rave_sp_register_event_notifier); + +static void csum_8b2c(const u8 *buf, size_t size, u8 *crc) +{ + *crc = *buf++; + size--; + + while (size--) + *crc += *buf++; + + *crc = 1 + ~(*crc); +} + +static void csum_ccitt(const u8 *buf, size_t size, u8 *crc) +{ + const u16 calculated = crc_ccitt_false(0xffff, buf, size); + + /* + * While the rest of the wire protocol is little-endian, + * CCITT-16 CRC in RDU2 device is sent out in big-endian order. + */ + put_unaligned_be16(calculated, crc); +} + +static void *stuff(unsigned char *dest, const unsigned char *src, size_t n) +{ + while (n--) { + const unsigned char byte = *src++; + + switch (byte) { + case RAVE_SP_STX: + case RAVE_SP_ETX: + case RAVE_SP_DLE: + *dest++ = RAVE_SP_DLE; + /* FALLTHROUGH */ + default: + *dest++ = byte; + } + } + + return dest; +} + +static int rave_sp_write(struct rave_sp *sp, const u8 *data, u8 data_size) +{ + const size_t checksum_length = sp->variant->checksum->length; + unsigned char frame[RAVE_SP_TX_BUFFER_SIZE]; + unsigned char crc[RAVE_SP_CHECKSUM_SIZE]; + unsigned char *dest = frame; + size_t length; + + if (WARN_ON(checksum_length > sizeof(crc))) + return -ENOMEM; + + if (WARN_ON(data_size > sizeof(frame))) + return -ENOMEM; + + sp->variant->checksum->subroutine(data, data_size, crc); + + *dest++ = RAVE_SP_STX; + dest = stuff(dest, data, data_size); + dest = stuff(dest, crc, checksum_length); + *dest++ = RAVE_SP_ETX; + + length = dest - frame; + + print_hex_dump(KERN_DEBUG, "rave-sp tx: ", DUMP_PREFIX_NONE, + 16, 1, frame, length, false); + + return serdev_device_write(sp->serdev, frame, length, HZ); +} + +static u8 rave_sp_reply_code(u8 command) +{ + /* + * There isn't a single rule that describes command code -> + * ACK code transformation, but, going through various + * versions of ICDs, there appear to be three distinct groups + * that can be described by simple transformation. + */ + switch (command) { + case 0xA0 ... 0xBE: + /* + * Commands implemented by firmware found in RDU1 and + * older devices all seem to obey the following rule + */ + return command + 0x20; + case 0xE0 ... 0xEF: + /* + * Events emitted by all versions of the firmare use + * least significant bit to get an ACK code + */ + return command | 0x01; + default: + /* + * Commands implemented by firmware found in RDU2 are + * similar to "old" commands, but they use slightly + * different offset + */ + return command + 0x40; + } +} + +int rave_sp_exec(struct rave_sp *sp, + void *__data, size_t data_size, + void *reply_data, size_t reply_data_size) +{ + struct rave_sp_reply reply = { + .data = reply_data, + .length = reply_data_size, + .received = COMPLETION_INITIALIZER_ONSTACK(reply.received), + }; + unsigned char *data = __data; + int command, ret = 0; + u8 ackid; + + command = sp->variant->cmd.translate(data[0]); + if (command < 0) + return command; + + ackid = atomic_inc_return(&sp->ackid); + reply.ackid = ackid; + reply.code = rave_sp_reply_code((u8)command), + + mutex_lock(&sp->bus_lock); + + mutex_lock(&sp->reply_lock); + sp->reply = &reply; + mutex_unlock(&sp->reply_lock); + + data[0] = command; + data[1] = ackid; + + rave_sp_write(sp, data, data_size); + + if (!wait_for_completion_timeout(&reply.received, HZ)) { + dev_err(&sp->serdev->dev, "Command timeout\n"); + ret = -ETIMEDOUT; + + mutex_lock(&sp->reply_lock); + sp->reply = NULL; + mutex_unlock(&sp->reply_lock); + } + + mutex_unlock(&sp->bus_lock); + return ret; +} +EXPORT_SYMBOL_GPL(rave_sp_exec); + +static void rave_sp_receive_event(struct rave_sp *sp, + const unsigned char *data, size_t length) +{ + u8 cmd[] = { + [0] = rave_sp_reply_code(data[0]), + [1] = data[1], + }; + + rave_sp_write(sp, cmd, sizeof(cmd)); + + blocking_notifier_call_chain(&sp->event_notifier_list, + rave_sp_action_pack(data[0], data[2]), + NULL); +} + +static void rave_sp_receive_reply(struct rave_sp *sp, + const unsigned char *data, size_t length) +{ + struct device *dev = &sp->serdev->dev; + struct rave_sp_reply *reply; + const size_t payload_length = length - 2; + + mutex_lock(&sp->reply_lock); + reply = sp->reply; + + if (reply) { + if (reply->code == data[0] && reply->ackid == data[1] && + payload_length >= reply->length) { + /* + * We are relying on memcpy(dst, src, 0) to be a no-op + * when handling commands that have a no-payload reply + */ + memcpy(reply->data, &data[2], reply->length); + complete(&reply->received); + sp->reply = NULL; + } else { + dev_err(dev, "Ignoring incorrect reply\n"); + dev_dbg(dev, "Code: expected = 0x%08x received = 0x%08x\n", + reply->code, data[0]); + dev_dbg(dev, "ACK ID: expected = 0x%08x received = 0x%08x\n", + reply->ackid, data[1]); + dev_dbg(dev, "Length: expected = %zu received = %zu\n", + reply->length, payload_length); + } + } + + mutex_unlock(&sp->reply_lock); +} + +static void rave_sp_receive_frame(struct rave_sp *sp, + const unsigned char *data, + size_t length) +{ + const size_t checksum_length = sp->variant->checksum->length; + const size_t payload_length = length - checksum_length; + const u8 *crc_reported = &data[payload_length]; + struct device *dev = &sp->serdev->dev; + u8 crc_calculated[checksum_length]; + + print_hex_dump(KERN_DEBUG, "rave-sp rx: ", DUMP_PREFIX_NONE, + 16, 1, data, length, false); + + if (unlikely(length <= checksum_length)) { + dev_warn(dev, "Dropping short frame\n"); + return; + } + + sp->variant->checksum->subroutine(data, payload_length, + crc_calculated); + + if (memcmp(crc_calculated, crc_reported, checksum_length)) { + dev_warn(dev, "Dropping bad frame\n"); + return; + } + + if (rave_sp_id_is_event(data[0])) + rave_sp_receive_event(sp, data, length); + else + rave_sp_receive_reply(sp, data, length); +} + +static int rave_sp_receive_buf(struct serdev_device *serdev, + const unsigned char *buf, size_t size) +{ + struct device *dev = &serdev->dev; + struct rave_sp *sp = dev_get_drvdata(dev); + struct rave_sp_deframer *deframer = &sp->deframer; + const unsigned char *src = buf; + const unsigned char *end = buf + size; + + while (src < end) { + const unsigned char byte = *src++; + + switch (deframer->state) { + case RAVE_SP_EXPECT_SOF: + if (byte == RAVE_SP_STX) + deframer->state = RAVE_SP_EXPECT_DATA; + break; + + case RAVE_SP_EXPECT_DATA: + /* + * Treat special byte values first + */ + switch (byte) { + case RAVE_SP_ETX: + rave_sp_receive_frame(sp, + deframer->data, + deframer->length); + /* + * Once we extracted a complete frame + * out of a stream, we call it done + * and proceed to bailing out while + * resetting the framer to initial + * state, regardless if we've consumed + * all of the stream or not. + */ + goto reset_framer; + case RAVE_SP_STX: + dev_warn(dev, "Bad frame: STX before ETX\n"); + /* + * If we encounter second "start of + * the frame" marker before seeing + * corresponding "end of frame", we + * reset the framer and ignore both: + * frame started by first SOF and + * frame started by current SOF. + * + * NOTE: The above means that only the + * frame started by third SOF, sent + * after this one will have a chance + * to get throught. + */ + goto reset_framer; + case RAVE_SP_DLE: + deframer->state = RAVE_SP_EXPECT_ESCAPED_DATA; + /* + * If we encounter escape sequence we + * need to skip it and collect the + * byte that follows. We do it by + * forcing the next iteration of the + * encompassing while loop. + */ + continue; + } + /* + * For the rest of the bytes, that are not + * speical snoflakes, we do the same thing + * that we do to escaped data - collect it in + * deframer buffer + */ + + /* FALLTHROUGH */ + + case RAVE_SP_EXPECT_ESCAPED_DATA: + deframer->data[deframer->length++] = byte; + + if (deframer->length == sizeof(deframer->data)) { + dev_warn(dev, "Bad frame: Too long\n"); + /* + * If the amount of data we've + * accumulated for current frame so + * far starts to exceed the capacity + * of deframer's buffer, there's + * nothing else we can do but to + * discard that data and start + * assemblying a new frame again + */ + goto reset_framer; + } + + /* + * We've extracted out special byte, now we + * can go back to regular data collecting + */ + deframer->state = RAVE_SP_EXPECT_DATA; + break; + } + } + + /* + * The only way to get out of the above loop and end up here + * is throught consuming all of the supplied data, so here we + * report that we processed it all. + */ + return size; + +reset_framer: + /* + * NOTE: A number of codepaths that will drop us here will do + * so before consuming all 'size' bytes of the data passed by + * serdev layer. We rely on the fact that serdev layer will + * re-execute this handler with the remainder of the Rx bytes + * once we report actual number of bytes that we processed. + */ + deframer->state = RAVE_SP_EXPECT_SOF; + deframer->length = 0; + + return src - buf; +} + +static int rave_sp_rdu1_cmd_translate(enum rave_sp_command command) +{ + if (command >= RAVE_SP_CMD_STATUS && + command <= RAVE_SP_CMD_CONTROL_EVENTS) + return command; + + return -EINVAL; +} + +static int rave_sp_rdu2_cmd_translate(enum rave_sp_command command) +{ + if (command >= RAVE_SP_CMD_GET_FIRMWARE_VERSION && + command <= RAVE_SP_CMD_GET_GPIO_STATE) + return command; + + if (command == RAVE_SP_CMD_REQ_COPPER_REV) { + /* + * As per RDU2 ICD 3.4.47 CMD_GET_COPPER_REV code is + * different from that for RDU1 and it is set to 0x28. + */ + return 0x28; + } + + return rave_sp_rdu1_cmd_translate(command); +} + +static int rave_sp_default_cmd_translate(enum rave_sp_command command) +{ + /* + * All of the following command codes were taken from "Table : + * Communications Protocol Message Types" in section 3.3 + * "MESSAGE TYPES" of Rave PIC24 ICD. + */ + switch (command) { + case RAVE_SP_CMD_GET_FIRMWARE_VERSION: + return 0x11; + case RAVE_SP_CMD_GET_BOOTLOADER_VERSION: + return 0x12; + case RAVE_SP_CMD_BOOT_SOURCE: + return 0x14; + case RAVE_SP_CMD_SW_WDT: + return 0x1C; + case RAVE_SP_CMD_RESET: + return 0x1E; + case RAVE_SP_CMD_RESET_REASON: + return 0x1F; + default: + return -EINVAL; + } +} + +static const struct rave_sp_checksum rave_sp_checksum_8b2c = { + .length = 1, + .subroutine = csum_8b2c, +}; + +static const struct rave_sp_checksum rave_sp_checksum_ccitt = { + .length = 2, + .subroutine = csum_ccitt, +}; + +static const struct rave_sp_variant rave_sp_legacy = { + .checksum = &rave_sp_checksum_8b2c, + .cmd = { + .translate = rave_sp_default_cmd_translate, + }, +}; + +static const struct rave_sp_variant rave_sp_rdu1 = { + .checksum = &rave_sp_checksum_8b2c, + .cmd = { + .translate = rave_sp_rdu1_cmd_translate, + }, +}; + +static const struct rave_sp_variant rave_sp_rdu2 = { + .checksum = &rave_sp_checksum_ccitt, + .cmd = { + .translate = rave_sp_rdu2_cmd_translate, + }, +}; + +static const struct of_device_id rave_sp_dt_ids[] = { + { .compatible = "zii,rave-sp-niu", .data = &rave_sp_legacy }, + { .compatible = "zii,rave-sp-mezz", .data = &rave_sp_legacy }, + { .compatible = "zii,rave-sp-esb", .data = &rave_sp_legacy }, + { .compatible = "zii,rave-sp-rdu1", .data = &rave_sp_rdu1 }, + { .compatible = "zii,rave-sp-rdu2", .data = &rave_sp_rdu2 }, + { /* sentinel */ } +}; + +static const struct serdev_device_ops rave_sp_serdev_device_ops = { + .receive_buf = rave_sp_receive_buf, + .write_wakeup = serdev_device_write_wakeup, +}; + +static int rave_sp_probe(struct serdev_device *serdev) +{ + struct device *dev = &serdev->dev; + struct rave_sp *sp; + u32 baud; + int ret; + + if (of_property_read_u32(dev->of_node, "current-speed", &baud)) { + dev_err(dev, + "'current-speed' is not specified in device node\n"); + return -EINVAL; + } + + sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL); + if (!sp) + return -ENOMEM; + + sp->serdev = serdev; + dev_set_drvdata(dev, sp); + + sp->variant = of_device_get_match_data(dev); + if (!sp->variant) + return -ENODEV; + + mutex_init(&sp->bus_lock); + mutex_init(&sp->reply_lock); + BLOCKING_INIT_NOTIFIER_HEAD(&sp->event_notifier_list); + + serdev_device_set_client_ops(serdev, &rave_sp_serdev_device_ops); + ret = devm_serdev_device_open(dev, serdev); + if (ret) + return ret; + + serdev_device_set_baudrate(serdev, baud); + + return devm_of_platform_populate(dev); +} + +MODULE_DEVICE_TABLE(of, rave_sp_dt_ids); + +static struct serdev_device_driver rave_sp_drv = { + .probe = rave_sp_probe, + .driver = { + .name = "rave-sp", + .of_match_table = rave_sp_dt_ids, + }, +}; +module_serdev_device_driver(rave_sp_drv); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andrey Vostrikov <andrey.vostrikov@cogentembedded.com>"); +MODULE_AUTHOR("Nikita Yushchenko <nikita.yoush@cogentembedded.com>"); +MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>"); +MODULE_DESCRIPTION("RAVE SP core driver"); diff --git a/drivers/mfd/stm32-lptimer.c b/drivers/mfd/stm32-lptimer.c index 075330a25f61..a00f99f36559 100644 --- a/drivers/mfd/stm32-lptimer.c +++ b/drivers/mfd/stm32-lptimer.c @@ -1,13 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * STM32 Low-Power Timer parent driver. - * * Copyright (C) STMicroelectronics 2017 - * * Author: Fabrice Gasnier <fabrice.gasnier@st.com> - * * Inspired by Benjamin Gaignard's stm32-timers driver - * - * License terms: GNU General Public License (GPL), version 2 */ #include <linux/mfd/stm32-lptimer.h> diff --git a/drivers/mfd/stm32-timers.c b/drivers/mfd/stm32-timers.c index a6675a449409..1d347e5dfa79 100644 --- a/drivers/mfd/stm32-timers.c +++ b/drivers/mfd/stm32-timers.c @@ -1,9 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics 2016 - * * Author: Benjamin Gaignard <benjamin.gaignard@st.com> - * - * License terms: GNU General Public License (GPL), version 2 */ #include <linux/mfd/stm32-timers.h> diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index 0f3fab47fe48..3cd958a31f36 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -124,7 +124,7 @@ static int ti_tscadc_probe(struct platform_device *pdev) struct ti_tscadc_dev *tscadc; struct resource *res; struct clk *clk; - struct device_node *node = pdev->dev.of_node; + struct device_node *node; struct mfd_cell *cell; struct property *prop; const __be32 *cur; diff --git a/drivers/mfd/tmio_core.c b/drivers/mfd/tmio_core.c index 83af78c1b0eb..ebf54cc28f7a 100644 --- a/drivers/mfd/tmio_core.c +++ b/drivers/mfd/tmio_core.c @@ -9,6 +9,26 @@ #include <linux/export.h> #include <linux/mfd/tmio.h> +#define CNF_CMD 0x04 +#define CNF_CTL_BASE 0x10 +#define CNF_INT_PIN 0x3d +#define CNF_STOP_CLK_CTL 0x40 +#define CNF_GCLK_CTL 0x41 +#define CNF_SD_CLK_MODE 0x42 +#define CNF_PIN_STATUS 0x44 +#define CNF_PWR_CTL_1 0x48 +#define CNF_PWR_CTL_2 0x49 +#define CNF_PWR_CTL_3 0x4a +#define CNF_CARD_DETECT_MODE 0x4c +#define CNF_SD_SLOT 0x50 +#define CNF_EXT_GCLK_CTL_1 0xf0 +#define CNF_EXT_GCLK_CTL_2 0xf1 +#define CNF_EXT_GCLK_CTL_3 0xf9 +#define CNF_SD_LED_EN_1 0xfa +#define CNF_SD_LED_EN_2 0xfe + +#define SDCREN 0x2 /* Enable access to MMC CTL regs. (flag in COMMAND_REG)*/ + int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base) { /* Enable the MMC/SD Control registers */ diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index f1a5c2357b14..7c0fa24f9067 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -496,6 +496,10 @@ config PCI_ENDPOINT_TEST Enable this configuration option to enable the host side test driver for PCI Endpoint. +config MISC_RTSX + tristate + default MISC_RTSX_PCI || MISC_RTSX_USB + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" @@ -508,4 +512,5 @@ source "drivers/misc/mic/Kconfig" source "drivers/misc/genwqe/Kconfig" source "drivers/misc/echo/Kconfig" source "drivers/misc/cxl/Kconfig" +source "drivers/misc/cardreader/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 5ca5f64df478..8d8cc096063b 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_CXL_BASE) += cxl/ obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o +obj-$(CONFIG_MISC_RTSX) += cardreader/ lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o diff --git a/drivers/misc/cardreader/Kconfig b/drivers/misc/cardreader/Kconfig new file mode 100644 index 000000000000..69e815e32a8c --- /dev/null +++ b/drivers/misc/cardreader/Kconfig @@ -0,0 +1,20 @@ +config MISC_RTSX_PCI + tristate "Realtek PCI-E card reader" + depends on PCI + select MFD_CORE + help + This supports for Realtek PCI-Express card reader including rts5209, + rts5227, rts522A, rts5229, rts5249, rts524A, rts525A, rtl8411, rts5260. + Realtek card readers support access to many types of memory cards, + such as Memory Stick, Memory Stick Pro, Secure Digital and + MultiMediaCard. + +config MISC_RTSX_USB + tristate "Realtek USB card reader" + depends on USB + select MFD_CORE + help + Select this option to get support for Realtek USB 2.0 card readers + including RTS5129, RTS5139, RTS5179 and RTS5170. + Realtek card reader supports access to many types of memory cards, + such as Memory Stick Pro, Secure Digital and MultiMediaCard. diff --git a/drivers/misc/cardreader/Makefile b/drivers/misc/cardreader/Makefile new file mode 100644 index 000000000000..9fabfcc6fa7a --- /dev/null +++ b/drivers/misc/cardreader/Makefile @@ -0,0 +1,4 @@ +rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o rts5260.o + +obj-$(CONFIG_MISC_RTSX_PCI) += rtsx_pci.o +obj-$(CONFIG_MISC_RTSX_USB) += rtsx_usb.o diff --git a/drivers/mfd/rtl8411.c b/drivers/misc/cardreader/rtl8411.c index b3ae6592014a..434fd070d3e3 100644 --- a/drivers/mfd/rtl8411.c +++ b/drivers/misc/cardreader/rtl8411.c @@ -23,7 +23,7 @@ #include <linux/module.h> #include <linux/bitops.h> #include <linux/delay.h> -#include <linux/mfd/rtsx_pci.h> +#include <linux/rtsx_pci.h> #include "rtsx_pcr.h" diff --git a/drivers/mfd/rts5209.c b/drivers/misc/cardreader/rts5209.c index b95beecf767f..ce68c48d8ec9 100644 --- a/drivers/mfd/rts5209.c +++ b/drivers/misc/cardreader/rts5209.c @@ -21,7 +21,7 @@ #include <linux/module.h> #include <linux/delay.h> -#include <linux/mfd/rtsx_pci.h> +#include <linux/rtsx_pci.h> #include "rtsx_pcr.h" diff --git a/drivers/mfd/rts5227.c b/drivers/misc/cardreader/rts5227.c index ff296a4bf3d2..024dcba8d6c8 100644 --- a/drivers/mfd/rts5227.c +++ b/drivers/misc/cardreader/rts5227.c @@ -22,7 +22,7 @@ #include <linux/module.h> #include <linux/delay.h> -#include <linux/mfd/rtsx_pci.h> +#include <linux/rtsx_pci.h> #include "rtsx_pcr.h" diff --git a/drivers/mfd/rts5229.c b/drivers/misc/cardreader/rts5229.c index 9ed9dc84eac8..9119261337cc 100644 --- a/drivers/mfd/rts5229.c +++ b/drivers/misc/cardreader/rts5229.c @@ -21,7 +21,7 @@ #include <linux/module.h> #include <linux/delay.h> -#include <linux/mfd/rtsx_pci.h> +#include <linux/rtsx_pci.h> #include "rtsx_pcr.h" diff --git a/drivers/mfd/rts5249.c b/drivers/misc/cardreader/rts5249.c index 7fcf37ba922c..dbe013abdb83 100644 --- a/drivers/mfd/rts5249.c +++ b/drivers/misc/cardreader/rts5249.c @@ -21,7 +21,7 @@ #include <linux/module.h> #include <linux/delay.h> -#include <linux/mfd/rtsx_pci.h> +#include <linux/rtsx_pci.h> #include "rtsx_pcr.h" @@ -738,4 +738,3 @@ void rts525a_init_params(struct rtsx_pcr *pcr) pcr->reg_pm_ctrl3 = RTS524A_PM_CTRL3; pcr->ops = &rts525a_pcr_ops; } - diff --git a/drivers/misc/cardreader/rts5260.c b/drivers/misc/cardreader/rts5260.c new file mode 100644 index 000000000000..07cb93abf685 --- /dev/null +++ b/drivers/misc/cardreader/rts5260.c @@ -0,0 +1,748 @@ +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2016-2017 Realtek Semiconductor Corp. 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; either version 2, or (at your option) any + * later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Author: + * Steven FENG <steven_feng@realsil.com.cn> + * Rui FENG <rui_feng@realsil.com.cn> + * Wei WANG <wei_wang@realsil.com.cn> + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/rtsx_pci.h> + +#include "rts5260.h" +#include "rtsx_pcr.h" + +static u8 rts5260_get_ic_version(struct rtsx_pcr *pcr) +{ + u8 val; + + rtsx_pci_read_register(pcr, DUMMY_REG_RESET_0, &val); + return val & IC_VERSION_MASK; +} + +static void rts5260_fill_driving(struct rtsx_pcr *pcr, u8 voltage) +{ + u8 driving_3v3[6][3] = { + {0x94, 0x94, 0x94}, + {0x11, 0x11, 0x18}, + {0x55, 0x55, 0x5C}, + {0x94, 0x94, 0x94}, + {0x94, 0x94, 0x94}, + {0xFF, 0xFF, 0xFF}, + }; + u8 driving_1v8[6][3] = { + {0x9A, 0x89, 0x89}, + {0xC4, 0xC4, 0xC4}, + {0x3C, 0x3C, 0x3C}, + {0x9B, 0x99, 0x99}, + {0x9A, 0x89, 0x89}, + {0xFE, 0xFE, 0xFE}, + }; + u8 (*driving)[3], drive_sel; + + if (voltage == OUTPUT_3V3) { + driving = driving_3v3; + drive_sel = pcr->sd30_drive_sel_3v3; + } else { + driving = driving_1v8; + drive_sel = pcr->sd30_drive_sel_1v8; + } + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CLK_DRIVE_SEL, + 0xFF, driving[drive_sel][0]); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CMD_DRIVE_SEL, + 0xFF, driving[drive_sel][1]); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DAT_DRIVE_SEL, + 0xFF, driving[drive_sel][2]); +} + +static void rtsx_base_fetch_vendor_settings(struct rtsx_pcr *pcr) +{ + u32 reg; + + rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); + + if (!rtsx_vendor_setting_valid(reg)) { + pcr_dbg(pcr, "skip fetch vendor setting\n"); + return; + } + + pcr->aspm_en = rtsx_reg_to_aspm(reg); + pcr->sd30_drive_sel_1v8 = rtsx_reg_to_sd30_drive_sel_1v8(reg); + pcr->card_drive_sel &= 0x3F; + pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg); + + rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); + pcr->sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3(reg); + if (rtsx_reg_check_reverse_socket(reg)) + pcr->flags |= PCR_REVERSE_SOCKET; +} + +static void rtsx_base_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) +{ + /* Set relink_time to 0 */ + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, MASK_8_BIT_DEF, 0); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 2, MASK_8_BIT_DEF, 0); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3, + RELINK_TIME_MASK, 0); + + if (pm_state == HOST_ENTER_S3) + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, + D3_DELINK_MODE_EN, D3_DELINK_MODE_EN); + + rtsx_pci_write_register(pcr, FPDCTL, ALL_POWER_DOWN, ALL_POWER_DOWN); +} + +static int rtsx_base_enable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, + LED_SHINE_MASK, LED_SHINE_EN); +} + +static int rtsx_base_disable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, + LED_SHINE_MASK, LED_SHINE_DISABLE); +} + +static int rts5260_turn_on_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, RTS5260_REG_GPIO_CTL0, + RTS5260_REG_GPIO_MASK, RTS5260_REG_GPIO_ON); +} + +static int rts5260_turn_off_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, RTS5260_REG_GPIO_CTL0, + RTS5260_REG_GPIO_MASK, RTS5260_REG_GPIO_OFF); +} + +/* SD Pull Control Enable: + * SD_DAT[3:0] ==> pull up + * SD_CD ==> pull up + * SD_WP ==> pull up + * SD_CMD ==> pull up + * SD_CLK ==> pull down + */ +static const u32 rts5260_sd_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL1, 0x66), + RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9), + RTSX_REG_PAIR(CARD_PULL_CTL4, 0xAA), + 0, +}; + +/* SD Pull Control Disable: + * SD_DAT[3:0] ==> pull down + * SD_CD ==> pull up + * SD_WP ==> pull down + * SD_CMD ==> pull down + * SD_CLK ==> pull down + */ +static const u32 rts5260_sd_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL1, 0x66), + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5), + RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55), + 0, +}; + +/* MS Pull Control Enable: + * MS CD ==> pull up + * others ==> pull down + */ +static const u32 rts5260_ms_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), + 0, +}; + +/* MS Pull Control Disable: + * MS CD ==> pull up + * others ==> pull down + */ +static const u32 rts5260_ms_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), + 0, +}; + +static int sd_set_sample_push_timing_sd30(struct rtsx_pcr *pcr) +{ + rtsx_pci_write_register(pcr, SD_CFG1, SD_MODE_SELECT_MASK + | SD_ASYNC_FIFO_NOT_RST, SD_30_MODE | SD_ASYNC_FIFO_NOT_RST); + rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, CLK_LOW_FREQ); + rtsx_pci_write_register(pcr, CARD_CLK_SOURCE, 0xFF, + CRC_VAR_CLK0 | SD30_FIX_CLK | SAMPLE_VAR_CLK1); + rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, 0); + + return 0; +} + +static int rts5260_card_power_on(struct rtsx_pcr *pcr, int card) +{ + int err = 0; + struct rtsx_cr_option *option = &pcr->option; + + if (option->ocp_en) + rtsx_pci_enable_ocp(pcr); + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_CONFIG2, + DV331812_VDD1, DV331812_VDD1); + err = rtsx_pci_send_cmd(pcr, CMD_TIMEOUT_DEF); + if (err < 0) + return err; + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_VCC_CFG0, + RTS5260_DVCC_TUNE_MASK, RTS5260_DVCC_33); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_VCC_CFG1, + LDO_POW_SDVDD1_MASK, LDO_POW_SDVDD1_ON); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_CONFIG2, + DV331812_POWERON, DV331812_POWERON); + err = rtsx_pci_send_cmd(pcr, CMD_TIMEOUT_DEF); + + msleep(20); + + if (pcr->extra_caps & EXTRA_CAPS_SD_SDR50 || + pcr->extra_caps & EXTRA_CAPS_SD_SDR104) + sd_set_sample_push_timing_sd30(pcr); + + /* Initialize SD_CFG1 register */ + rtsx_pci_write_register(pcr, SD_CFG1, 0xFF, + SD_CLK_DIVIDE_128 | SD_20_MODE); + + rtsx_pci_write_register(pcr, SD_SAMPLE_POINT_CTL, + 0xFF, SD20_RX_POS_EDGE); + rtsx_pci_write_register(pcr, SD_PUSH_POINT_CTL, 0xFF, 0); + rtsx_pci_write_register(pcr, CARD_STOP, SD_STOP | SD_CLR_ERR, + SD_STOP | SD_CLR_ERR); + + /* Reset SD_CFG3 register */ + rtsx_pci_write_register(pcr, SD_CFG3, SD30_CLK_END_EN, 0); + rtsx_pci_write_register(pcr, REG_SD_STOP_SDCLK_CFG, + SD30_CLK_STOP_CFG_EN | SD30_CLK_STOP_CFG1 | + SD30_CLK_STOP_CFG0, 0); + + rtsx_pci_write_register(pcr, REG_PRE_RW_MODE, EN_INFINITE_MODE, 0); + + return err; +} + +static int rts5260_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +{ + switch (voltage) { + case OUTPUT_3V3: + rtsx_pci_write_register(pcr, LDO_CONFIG2, + DV331812_VDD1, DV331812_VDD1); + rtsx_pci_write_register(pcr, LDO_DV18_CFG, + DV331812_MASK, DV331812_33); + rtsx_pci_write_register(pcr, SD_PAD_CTL, SD_IO_USING_1V8, 0); + break; + case OUTPUT_1V8: + rtsx_pci_write_register(pcr, LDO_CONFIG2, + DV331812_VDD1, DV331812_VDD1); + rtsx_pci_write_register(pcr, LDO_DV18_CFG, + DV331812_MASK, DV331812_17); + rtsx_pci_write_register(pcr, SD_PAD_CTL, SD_IO_USING_1V8, + SD_IO_USING_1V8); + break; + default: + return -EINVAL; + } + + /* set pad drive */ + rtsx_pci_init_cmd(pcr); + rts5260_fill_driving(pcr, voltage); + return rtsx_pci_send_cmd(pcr, CMD_TIMEOUT_DEF); +} + +static void rts5260_stop_cmd(struct rtsx_pcr *pcr) +{ + rtsx_pci_writel(pcr, RTSX_HCBCTLR, STOP_CMD); + rtsx_pci_writel(pcr, RTSX_HDBCTLR, STOP_DMA); + rtsx_pci_write_register(pcr, RTS5260_DMA_RST_CTL_0, + RTS5260_DMA_RST | RTS5260_ADMA3_RST, + RTS5260_DMA_RST | RTS5260_ADMA3_RST); + rtsx_pci_write_register(pcr, RBCTL, RB_FLUSH, RB_FLUSH); +} + +static void rts5260_card_before_power_off(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + + rts5260_stop_cmd(pcr); + rts5260_switch_output_voltage(pcr, OUTPUT_3V3); + + if (option->ocp_en) + rtsx_pci_disable_ocp(pcr); +} + +static int rts5260_card_power_off(struct rtsx_pcr *pcr, int card) +{ + int err = 0; + + rts5260_card_before_power_off(pcr); + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_VCC_CFG1, + LDO_POW_SDVDD1_MASK, LDO_POW_SDVDD1_OFF); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_CONFIG2, + DV331812_POWERON, DV331812_POWEROFF); + err = rtsx_pci_send_cmd(pcr, CMD_TIMEOUT_DEF); + + return err; +} + +static void rts5260_init_ocp(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + + if (option->ocp_en) { + u8 mask, val; + + rtsx_pci_write_register(pcr, RTS5260_DVCC_CTRL, + RTS5260_DVCC_OCP_EN | + RTS5260_DVCC_OCP_CL_EN, + RTS5260_DVCC_OCP_EN | + RTS5260_DVCC_OCP_CL_EN); + rtsx_pci_write_register(pcr, RTS5260_DVIO_CTRL, + RTS5260_DVIO_OCP_EN | + RTS5260_DVIO_OCP_CL_EN, + RTS5260_DVIO_OCP_EN | + RTS5260_DVIO_OCP_CL_EN); + + rtsx_pci_write_register(pcr, RTS5260_DVCC_CTRL, + RTS5260_DVCC_OCP_THD_MASK, + option->sd_400mA_ocp_thd); + + rtsx_pci_write_register(pcr, RTS5260_DVIO_CTRL, + RTS5260_DVIO_OCP_THD_MASK, + RTS5260_DVIO_OCP_THD_350); + + rtsx_pci_write_register(pcr, RTS5260_DV331812_CFG, + RTS5260_DV331812_OCP_THD_MASK, + RTS5260_DV331812_OCP_THD_210); + + mask = SD_OCP_GLITCH_MASK | SDVIO_OCP_GLITCH_MASK; + val = pcr->hw_param.ocp_glitch; + rtsx_pci_write_register(pcr, REG_OCPGLITCH, mask, val); + + rtsx_pci_enable_ocp(pcr); + } else { + rtsx_pci_write_register(pcr, RTS5260_DVCC_CTRL, + RTS5260_DVCC_OCP_EN | + RTS5260_DVCC_OCP_CL_EN, 0); + rtsx_pci_write_register(pcr, RTS5260_DVIO_CTRL, + RTS5260_DVIO_OCP_EN | + RTS5260_DVIO_OCP_CL_EN, 0); + } +} + +static void rts5260_enable_ocp(struct rtsx_pcr *pcr) +{ + u8 val = 0; + + rtsx_pci_write_register(pcr, FPDCTL, OC_POWER_DOWN, 0); + + val = SD_OCP_INT_EN | SD_DETECT_EN; + val |= SDVIO_OCP_INT_EN | SDVIO_DETECT_EN; + rtsx_pci_write_register(pcr, REG_OCPCTL, 0xFF, val); + rtsx_pci_write_register(pcr, REG_DV3318_OCPCTL, + DV3318_DETECT_EN | DV3318_OCP_INT_EN, + DV3318_DETECT_EN | DV3318_OCP_INT_EN); +} + +static void rts5260_disable_ocp(struct rtsx_pcr *pcr) +{ + u8 mask = 0; + + mask = SD_OCP_INT_EN | SD_DETECT_EN; + mask |= SDVIO_OCP_INT_EN | SDVIO_DETECT_EN; + rtsx_pci_write_register(pcr, REG_OCPCTL, mask, 0); + rtsx_pci_write_register(pcr, REG_DV3318_OCPCTL, + DV3318_DETECT_EN | DV3318_OCP_INT_EN, 0); + + rtsx_pci_write_register(pcr, FPDCTL, OC_POWER_DOWN, + OC_POWER_DOWN); +} + +int rts5260_get_ocpstat(struct rtsx_pcr *pcr, u8 *val) +{ + return rtsx_pci_read_register(pcr, REG_OCPSTAT, val); +} + +int rts5260_get_ocpstat2(struct rtsx_pcr *pcr, u8 *val) +{ + return rtsx_pci_read_register(pcr, REG_DV3318_OCPSTAT, val); +} + +void rts5260_clear_ocpstat(struct rtsx_pcr *pcr) +{ + u8 mask = 0; + u8 val = 0; + + mask = SD_OCP_INT_CLR | SD_OC_CLR; + mask |= SDVIO_OCP_INT_CLR | SDVIO_OC_CLR; + val = SD_OCP_INT_CLR | SD_OC_CLR; + val |= SDVIO_OCP_INT_CLR | SDVIO_OC_CLR; + + rtsx_pci_write_register(pcr, REG_OCPCTL, mask, val); + rtsx_pci_write_register(pcr, REG_DV3318_OCPCTL, + DV3318_OCP_INT_CLR | DV3318_OCP_CLR, + DV3318_OCP_INT_CLR | DV3318_OCP_CLR); + udelay(10); + rtsx_pci_write_register(pcr, REG_OCPCTL, mask, 0); + rtsx_pci_write_register(pcr, REG_DV3318_OCPCTL, + DV3318_OCP_INT_CLR | DV3318_OCP_CLR, 0); +} + +void rts5260_process_ocp(struct rtsx_pcr *pcr) +{ + if (!pcr->option.ocp_en) + return; + + rtsx_pci_get_ocpstat(pcr, &pcr->ocp_stat); + rts5260_get_ocpstat2(pcr, &pcr->ocp_stat2); + if (pcr->card_exist & SD_EXIST) + rtsx_sd_power_off_card3v3(pcr); + else if (pcr->card_exist & MS_EXIST) + rtsx_ms_power_off_card3v3(pcr); + + if (!(pcr->card_exist & MS_EXIST) && !(pcr->card_exist & SD_EXIST)) { + if ((pcr->ocp_stat & (SD_OC_NOW | SD_OC_EVER | + SDVIO_OC_NOW | SDVIO_OC_EVER)) || + (pcr->ocp_stat2 & (DV3318_OCP_NOW | DV3318_OCP_EVER))) + rtsx_pci_clear_ocpstat(pcr); + pcr->ocp_stat = 0; + pcr->ocp_stat2 = 0; + } + + if ((pcr->ocp_stat & (SD_OC_NOW | SD_OC_EVER | + SDVIO_OC_NOW | SDVIO_OC_EVER)) || + (pcr->ocp_stat2 & (DV3318_OCP_NOW | DV3318_OCP_EVER))) { + if (pcr->card_exist & SD_EXIST) + rtsx_pci_write_register(pcr, CARD_OE, SD_OUTPUT_EN, 0); + else if (pcr->card_exist & MS_EXIST) + rtsx_pci_write_register(pcr, CARD_OE, MS_OUTPUT_EN, 0); + } +} + +int rts5260_init_hw(struct rtsx_pcr *pcr) +{ + int err; + + rtsx_pci_init_ocp(pcr); + + rtsx_pci_init_cmd(pcr); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, L1SUB_CONFIG1, + AUX_CLK_ACTIVE_SEL_MASK, MAC_CKSW_DONE); + /* Rest L1SUB Config */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, L1SUB_CONFIG3, 0xFF, 0x00); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PM_CLK_FORCE_CTL, + CLK_PM_EN, CLK_PM_EN); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWD_SUSPEND_EN, 0xFF, 0xFF); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + PWR_GATE_EN, PWR_GATE_EN); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, REG_VREF, + PWD_SUSPND_EN, PWD_SUSPND_EN); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RBCTL, + U_AUTO_DMA_EN_MASK, U_AUTO_DMA_DISABLE); + + if (pcr->flags & PCR_REVERSE_SOCKET) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB0, 0xB0); + else + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB0, 0x80); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OBFF_CFG, + OBFF_EN_MASK, OBFF_DISABLE); + + err = rtsx_pci_send_cmd(pcr, CMD_TIMEOUT_DEF); + if (err < 0) + return err; + + return 0; +} + +static void rts5260_pwr_saving_setting(struct rtsx_pcr *pcr) +{ + int lss_l1_1, lss_l1_2; + + lss_l1_1 = rtsx_check_dev_flag(pcr, ASPM_L1_1_EN) + | rtsx_check_dev_flag(pcr, PM_L1_1_EN); + lss_l1_2 = rtsx_check_dev_flag(pcr, ASPM_L1_2_EN) + | rtsx_check_dev_flag(pcr, PM_L1_2_EN); + + if (lss_l1_2) { + pcr_dbg(pcr, "Set parameters for L1.2."); + rtsx_pci_write_register(pcr, PWR_GLOBAL_CTRL, + 0xFF, PCIE_L1_2_EN); + rtsx_pci_write_register(pcr, PWR_FE_CTL, + 0xFF, PCIE_L1_2_PD_FE_EN); + } else if (lss_l1_1) { + pcr_dbg(pcr, "Set parameters for L1.1."); + rtsx_pci_write_register(pcr, PWR_GLOBAL_CTRL, + 0xFF, PCIE_L1_1_EN); + rtsx_pci_write_register(pcr, PWR_FE_CTL, + 0xFF, PCIE_L1_1_PD_FE_EN); + } else { + pcr_dbg(pcr, "Set parameters for L1."); + rtsx_pci_write_register(pcr, PWR_GLOBAL_CTRL, + 0xFF, PCIE_L1_0_EN); + rtsx_pci_write_register(pcr, PWR_FE_CTL, + 0xFF, PCIE_L1_0_PD_FE_EN); + } + + rtsx_pci_write_register(pcr, CFG_L1_0_PCIE_DPHY_RET_VALUE, + 0xFF, CFG_L1_0_RET_VALUE_DEFAULT); + rtsx_pci_write_register(pcr, CFG_L1_0_PCIE_MAC_RET_VALUE, + 0xFF, CFG_L1_0_RET_VALUE_DEFAULT); + rtsx_pci_write_register(pcr, CFG_L1_0_CRC_SD30_RET_VALUE, + 0xFF, CFG_L1_0_RET_VALUE_DEFAULT); + rtsx_pci_write_register(pcr, CFG_L1_0_CRC_SD40_RET_VALUE, + 0xFF, CFG_L1_0_RET_VALUE_DEFAULT); + rtsx_pci_write_register(pcr, CFG_L1_0_SYS_RET_VALUE, + 0xFF, CFG_L1_0_RET_VALUE_DEFAULT); + /*Option cut APHY*/ + rtsx_pci_write_register(pcr, CFG_PCIE_APHY_OFF_0, + 0xFF, CFG_PCIE_APHY_OFF_0_DEFAULT); + rtsx_pci_write_register(pcr, CFG_PCIE_APHY_OFF_1, + 0xFF, CFG_PCIE_APHY_OFF_1_DEFAULT); + rtsx_pci_write_register(pcr, CFG_PCIE_APHY_OFF_2, + 0xFF, CFG_PCIE_APHY_OFF_2_DEFAULT); + rtsx_pci_write_register(pcr, CFG_PCIE_APHY_OFF_3, + 0xFF, CFG_PCIE_APHY_OFF_3_DEFAULT); + /*CDR DEC*/ + rtsx_pci_write_register(pcr, PWC_CDR, 0xFF, PWC_CDR_DEFAULT); + /*PWMPFM*/ + rtsx_pci_write_register(pcr, CFG_LP_FPWM_VALUE, + 0xFF, CFG_LP_FPWM_VALUE_DEFAULT); + /*No Power Saving WA*/ + rtsx_pci_write_register(pcr, CFG_L1_0_CRC_MISC_RET_VALUE, + 0xFF, CFG_L1_0_CRC_MISC_RET_VALUE_DEFAULT); +} + +static void rts5260_init_from_cfg(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + u32 lval; + + rtsx_pci_read_config_dword(pcr, PCR_ASPM_SETTING_5260, &lval); + + if (lval & ASPM_L1_1_EN_MASK) + rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); + + if (lval & ASPM_L1_2_EN_MASK) + rtsx_set_dev_flag(pcr, ASPM_L1_2_EN); + + if (lval & PM_L1_1_EN_MASK) + rtsx_set_dev_flag(pcr, PM_L1_1_EN); + + if (lval & PM_L1_2_EN_MASK) + rtsx_set_dev_flag(pcr, PM_L1_2_EN); + + rts5260_pwr_saving_setting(pcr); + + if (option->ltr_en) { + u16 val; + + pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &val); + if (val & PCI_EXP_DEVCTL2_LTR_EN) { + option->ltr_enabled = true; + option->ltr_active = true; + rtsx_set_ltr_latency(pcr, option->ltr_active_latency); + } else { + option->ltr_enabled = false; + } + } + + if (rtsx_check_dev_flag(pcr, ASPM_L1_1_EN | ASPM_L1_2_EN + | PM_L1_1_EN | PM_L1_2_EN)) + option->force_clkreq_0 = false; + else + option->force_clkreq_0 = true; +} + +static int rts5260_extra_init_hw(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + + /* Set mcu_cnt to 7 to ensure data can be sampled properly */ + rtsx_pci_write_register(pcr, 0xFC03, 0x7F, 0x07); + rtsx_pci_write_register(pcr, SSC_DIV_N_0, 0xFF, 0x5D); + + rts5260_init_from_cfg(pcr); + + /* force no MDIO*/ + rtsx_pci_write_register(pcr, RTS5260_AUTOLOAD_CFG4, + 0xFF, RTS5260_MIMO_DISABLE); + /*Modify SDVCC Tune Default Parameters!*/ + rtsx_pci_write_register(pcr, LDO_VCC_CFG0, + RTS5260_DVCC_TUNE_MASK, RTS5260_DVCC_33); + + rtsx_pci_write_register(pcr, PCLK_CTL, PCLK_MODE_SEL, PCLK_MODE_SEL); + + rts5260_init_hw(pcr); + + /* + * If u_force_clkreq_0 is enabled, CLKREQ# PIN will be forced + * to drive low, and we forcibly request clock. + */ + if (option->force_clkreq_0) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, + FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW); + else + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, + FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH); + + return 0; +} + +void rts5260_set_aspm(struct rtsx_pcr *pcr, bool enable) +{ + struct rtsx_cr_option *option = &pcr->option; + u8 val = 0; + + if (pcr->aspm_enabled == enable) + return; + + if (option->dev_aspm_mode == DEV_ASPM_DYNAMIC) { + if (enable) + val = pcr->aspm_en; + rtsx_pci_update_cfg_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL, + ASPM_MASK_NEG, val); + } else if (option->dev_aspm_mode == DEV_ASPM_BACKDOOR) { + u8 mask = FORCE_ASPM_VAL_MASK | FORCE_ASPM_CTL0; + + if (!enable) + val = FORCE_ASPM_CTL0; + rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, mask, val); + } + + pcr->aspm_enabled = enable; +} + +static void rts5260_set_l1off_cfg_sub_d0(struct rtsx_pcr *pcr, int active) +{ + struct rtsx_cr_option *option = &pcr->option; + u32 interrupt = rtsx_pci_readl(pcr, RTSX_BIPR); + int card_exist = (interrupt & SD_EXIST) | (interrupt & MS_EXIST); + int aspm_L1_1, aspm_L1_2; + u8 val = 0; + + aspm_L1_1 = rtsx_check_dev_flag(pcr, ASPM_L1_1_EN); + aspm_L1_2 = rtsx_check_dev_flag(pcr, ASPM_L1_2_EN); + + if (active) { + /* run, latency: 60us */ + if (aspm_L1_1) + val = option->ltr_l1off_snooze_sspwrgate; + } else { + /* l1off, latency: 300us */ + if (aspm_L1_2) + val = option->ltr_l1off_sspwrgate; + } + + if (aspm_L1_1 || aspm_L1_2) { + if (rtsx_check_dev_flag(pcr, + LTR_L1SS_PWR_GATE_CHECK_CARD_EN)) { + if (card_exist) + val &= ~L1OFF_MBIAS2_EN_5250; + else + val |= L1OFF_MBIAS2_EN_5250; + } + } + rtsx_set_l1off_sub(pcr, val); +} + +static const struct pcr_ops rts5260_pcr_ops = { + .fetch_vendor_settings = rtsx_base_fetch_vendor_settings, + .turn_on_led = rts5260_turn_on_led, + .turn_off_led = rts5260_turn_off_led, + .extra_init_hw = rts5260_extra_init_hw, + .enable_auto_blink = rtsx_base_enable_auto_blink, + .disable_auto_blink = rtsx_base_disable_auto_blink, + .card_power_on = rts5260_card_power_on, + .card_power_off = rts5260_card_power_off, + .switch_output_voltage = rts5260_switch_output_voltage, + .force_power_down = rtsx_base_force_power_down, + .stop_cmd = rts5260_stop_cmd, + .set_aspm = rts5260_set_aspm, + .set_l1off_cfg_sub_d0 = rts5260_set_l1off_cfg_sub_d0, + .enable_ocp = rts5260_enable_ocp, + .disable_ocp = rts5260_disable_ocp, + .init_ocp = rts5260_init_ocp, + .process_ocp = rts5260_process_ocp, + .get_ocpstat = rts5260_get_ocpstat, + .clear_ocpstat = rts5260_clear_ocpstat, +}; + +void rts5260_init_params(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + struct rtsx_hw_param *hw_param = &pcr->hw_param; + + pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; + pcr->num_slots = 2; + + pcr->flags = 0; + pcr->card_drive_sel = RTSX_CARD_DRIVE_DEFAULT; + pcr->sd30_drive_sel_1v8 = CFG_DRIVER_TYPE_B; + pcr->sd30_drive_sel_3v3 = CFG_DRIVER_TYPE_B; + pcr->aspm_en = ASPM_L1_EN; + pcr->tx_initial_phase = SET_CLOCK_PHASE(1, 29, 16); + pcr->rx_initial_phase = SET_CLOCK_PHASE(24, 6, 5); + + pcr->ic_version = rts5260_get_ic_version(pcr); + pcr->sd_pull_ctl_enable_tbl = rts5260_sd_pull_ctl_enable_tbl; + pcr->sd_pull_ctl_disable_tbl = rts5260_sd_pull_ctl_disable_tbl; + pcr->ms_pull_ctl_enable_tbl = rts5260_ms_pull_ctl_enable_tbl; + pcr->ms_pull_ctl_disable_tbl = rts5260_ms_pull_ctl_disable_tbl; + + pcr->reg_pm_ctrl3 = RTS524A_PM_CTRL3; + + pcr->ops = &rts5260_pcr_ops; + + option->dev_flags = (LTR_L1SS_PWR_GATE_CHECK_CARD_EN + | LTR_L1SS_PWR_GATE_EN); + option->ltr_en = true; + + /* init latency of active, idle, L1OFF to 60us, 300us, 3ms */ + option->ltr_active_latency = LTR_ACTIVE_LATENCY_DEF; + option->ltr_idle_latency = LTR_IDLE_LATENCY_DEF; + option->ltr_l1off_latency = LTR_L1OFF_LATENCY_DEF; + option->dev_aspm_mode = DEV_ASPM_DYNAMIC; + option->l1_snooze_delay = L1_SNOOZE_DELAY_DEF; + option->ltr_l1off_sspwrgate = LTR_L1OFF_SSPWRGATE_5250_DEF; + option->ltr_l1off_snooze_sspwrgate = + LTR_L1OFF_SNOOZE_SSPWRGATE_5250_DEF; + + option->ocp_en = 1; + if (option->ocp_en) + hw_param->interrupt_en |= SD_OC_INT_EN; + hw_param->ocp_glitch = SD_OCP_GLITCH_10M | SDVIO_OCP_GLITCH_800U; + option->sd_400mA_ocp_thd = RTS5260_DVCC_OCP_THD_550; + option->sd_800mA_ocp_thd = RTS5260_DVCC_OCP_THD_970; +} diff --git a/drivers/misc/cardreader/rts5260.h b/drivers/misc/cardreader/rts5260.h new file mode 100644 index 000000000000..53a1411c8868 --- /dev/null +++ b/drivers/misc/cardreader/rts5260.h @@ -0,0 +1,45 @@ +#ifndef __RTS5260_H__ +#define __RTS5260_H__ + +#define RTS5260_DVCC_CTRL 0xFF73 +#define RTS5260_DVCC_OCP_EN (0x01 << 7) +#define RTS5260_DVCC_OCP_THD_MASK (0x07 << 4) +#define RTS5260_DVCC_POWERON (0x01 << 3) +#define RTS5260_DVCC_OCP_CL_EN (0x01 << 2) + +#define RTS5260_DVIO_CTRL 0xFF75 +#define RTS5260_DVIO_OCP_EN (0x01 << 7) +#define RTS5260_DVIO_OCP_THD_MASK (0x07 << 4) +#define RTS5260_DVIO_POWERON (0x01 << 3) +#define RTS5260_DVIO_OCP_CL_EN (0x01 << 2) + +#define RTS5260_DV331812_CFG 0xFF71 +#define RTS5260_DV331812_OCP_EN (0x01 << 7) +#define RTS5260_DV331812_OCP_THD_MASK (0x07 << 4) +#define RTS5260_DV331812_POWERON (0x01 << 3) +#define RTS5260_DV331812_SEL (0x01 << 2) +#define RTS5260_DV331812_VDD1 (0x01 << 2) +#define RTS5260_DV331812_VDD2 (0x00 << 2) + +#define RTS5260_DV331812_OCP_THD_120 (0x00 << 4) +#define RTS5260_DV331812_OCP_THD_140 (0x01 << 4) +#define RTS5260_DV331812_OCP_THD_160 (0x02 << 4) +#define RTS5260_DV331812_OCP_THD_180 (0x03 << 4) +#define RTS5260_DV331812_OCP_THD_210 (0x04 << 4) +#define RTS5260_DV331812_OCP_THD_240 (0x05 << 4) +#define RTS5260_DV331812_OCP_THD_270 (0x06 << 4) +#define RTS5260_DV331812_OCP_THD_300 (0x07 << 4) + +#define RTS5260_DVIO_OCP_THD_250 (0x00 << 4) +#define RTS5260_DVIO_OCP_THD_300 (0x01 << 4) +#define RTS5260_DVIO_OCP_THD_350 (0x02 << 4) +#define RTS5260_DVIO_OCP_THD_400 (0x03 << 4) +#define RTS5260_DVIO_OCP_THD_450 (0x04 << 4) +#define RTS5260_DVIO_OCP_THD_500 (0x05 << 4) +#define RTS5260_DVIO_OCP_THD_550 (0x06 << 4) +#define RTS5260_DVIO_OCP_THD_600 (0x07 << 4) + +#define RTS5260_DVCC_OCP_THD_550 (0x00 << 4) +#define RTS5260_DVCC_OCP_THD_970 (0x05 << 4) + +#endif diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index c3ed885c155c..fd09b0960097 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -29,7 +29,7 @@ #include <linux/idr.h> #include <linux/platform_device.h> #include <linux/mfd/core.h> -#include <linux/mfd/rtsx_pci.h> +#include <linux/rtsx_pci.h> #include <linux/mmc/card.h> #include <asm/unaligned.h> @@ -62,6 +62,7 @@ static const struct pci_device_id rtsx_pci_ids[] = { { PCI_DEVICE(0x10EC, 0x5286), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x524A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x525A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5260), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { 0, } }; @@ -334,6 +335,9 @@ EXPORT_SYMBOL_GPL(rtsx_pci_read_phy_register); void rtsx_pci_stop_cmd(struct rtsx_pcr *pcr) { + if (pcr->ops->stop_cmd) + return pcr->ops->stop_cmd(pcr); + rtsx_pci_writel(pcr, RTSX_HCBCTLR, STOP_CMD); rtsx_pci_writel(pcr, RTSX_HDBCTLR, STOP_DMA); @@ -826,7 +830,7 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, return err; /* Wait SSC clock stable */ - udelay(10); + udelay(SSC_CLOCK_STABLE_WAIT); err = rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, 0); if (err < 0) return err; @@ -963,6 +967,20 @@ static void rtsx_pci_card_detect(struct work_struct *work) pcr->slots[RTSX_MS_CARD].p_dev); } +void rtsx_pci_process_ocp(struct rtsx_pcr *pcr) +{ + if (pcr->ops->process_ocp) + pcr->ops->process_ocp(pcr); +} + +int rtsx_pci_process_ocp_interrupt(struct rtsx_pcr *pcr) +{ + if (pcr->option.ocp_en) + rtsx_pci_process_ocp(pcr); + + return 0; +} + static irqreturn_t rtsx_pci_isr(int irq, void *dev_id) { struct rtsx_pcr *pcr = dev_id; @@ -987,6 +1005,9 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id) int_reg &= (pcr->bier | 0x7FFFFF); + if (int_reg & SD_OC_INT) + rtsx_pci_process_ocp_interrupt(pcr); + if (int_reg & SD_INT) { if (int_reg & SD_EXIST) { pcr->card_inserted |= SD_EXIST; @@ -1119,6 +1140,102 @@ static void rtsx_pci_power_off(struct rtsx_pcr *pcr, u8 pm_state) } #endif +void rtsx_pci_enable_ocp(struct rtsx_pcr *pcr) +{ + u8 val = SD_OCP_INT_EN | SD_DETECT_EN; + + if (pcr->ops->enable_ocp) + pcr->ops->enable_ocp(pcr); + else + rtsx_pci_write_register(pcr, REG_OCPCTL, 0xFF, val); + +} + +void rtsx_pci_disable_ocp(struct rtsx_pcr *pcr) +{ + u8 mask = SD_OCP_INT_EN | SD_DETECT_EN; + + if (pcr->ops->disable_ocp) + pcr->ops->disable_ocp(pcr); + else + rtsx_pci_write_register(pcr, REG_OCPCTL, mask, 0); +} + +void rtsx_pci_init_ocp(struct rtsx_pcr *pcr) +{ + if (pcr->ops->init_ocp) { + pcr->ops->init_ocp(pcr); + } else { + struct rtsx_cr_option *option = &(pcr->option); + + if (option->ocp_en) { + u8 val = option->sd_400mA_ocp_thd; + + rtsx_pci_write_register(pcr, FPDCTL, OC_POWER_DOWN, 0); + rtsx_pci_write_register(pcr, REG_OCPPARA1, + SD_OCP_TIME_MASK, SD_OCP_TIME_800); + rtsx_pci_write_register(pcr, REG_OCPPARA2, + SD_OCP_THD_MASK, val); + rtsx_pci_write_register(pcr, REG_OCPGLITCH, + SD_OCP_GLITCH_MASK, pcr->hw_param.ocp_glitch); + rtsx_pci_enable_ocp(pcr); + } else { + /* OC power down */ + rtsx_pci_write_register(pcr, FPDCTL, OC_POWER_DOWN, + OC_POWER_DOWN); + } + } +} + +int rtsx_pci_get_ocpstat(struct rtsx_pcr *pcr, u8 *val) +{ + if (pcr->ops->get_ocpstat) + return pcr->ops->get_ocpstat(pcr, val); + else + return rtsx_pci_read_register(pcr, REG_OCPSTAT, val); +} + +void rtsx_pci_clear_ocpstat(struct rtsx_pcr *pcr) +{ + if (pcr->ops->clear_ocpstat) { + pcr->ops->clear_ocpstat(pcr); + } else { + u8 mask = SD_OCP_INT_CLR | SD_OC_CLR; + u8 val = SD_OCP_INT_CLR | SD_OC_CLR; + + rtsx_pci_write_register(pcr, REG_OCPCTL, mask, val); + rtsx_pci_write_register(pcr, REG_OCPCTL, mask, 0); + } +} + +int rtsx_sd_power_off_card3v3(struct rtsx_pcr *pcr) +{ + rtsx_pci_write_register(pcr, CARD_CLK_EN, SD_CLK_EN | + MS_CLK_EN | SD40_CLK_EN, 0); + rtsx_pci_write_register(pcr, CARD_OE, SD_OUTPUT_EN, 0); + + rtsx_pci_card_power_off(pcr, RTSX_SD_CARD); + + msleep(50); + + rtsx_pci_card_pull_ctl_disable(pcr, RTSX_SD_CARD); + + return 0; +} + +int rtsx_ms_power_off_card3v3(struct rtsx_pcr *pcr) +{ + rtsx_pci_write_register(pcr, CARD_CLK_EN, SD_CLK_EN | + MS_CLK_EN | SD40_CLK_EN, 0); + + rtsx_pci_card_pull_ctl_disable(pcr, RTSX_MS_CARD); + + rtsx_pci_write_register(pcr, CARD_OE, MS_OUTPUT_EN, 0); + rtsx_pci_card_power_off(pcr, RTSX_MS_CARD); + + return 0; +} + static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) { int err; @@ -1189,6 +1306,7 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) case PID_5250: case PID_524A: case PID_525A: + case PID_5260: rtsx_pci_write_register(pcr, PM_CLK_FORCE_CTL, 1, 1); break; default: @@ -1265,6 +1383,9 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) case 0x5286: rtl8402_init_params(pcr); break; + case 0x5260: + rts5260_init_params(pcr); + break; } pcr_dbg(pcr, "PID: 0x%04x, IC version: 0x%02x\n", diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/misc/cardreader/rtsx_pcr.h index ec784e04fe20..6ea1655db0bb 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/misc/cardreader/rtsx_pcr.h @@ -22,7 +22,7 @@ #ifndef __RTSX_PCR_H #define __RTSX_PCR_H -#include <linux/mfd/rtsx_pci.h> +#include <linux/rtsx_pci.h> #define MIN_DIV_N_PCR 80 #define MAX_DIV_N_PCR 208 @@ -44,6 +44,8 @@ #define ASPM_MASK_NEG 0xFC #define MASK_8_BIT_DEF 0xFF +#define SSC_CLOCK_STABLE_WAIT 130 + int __rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val); int __rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val); @@ -57,6 +59,7 @@ void rts5249_init_params(struct rtsx_pcr *pcr); void rts524a_init_params(struct rtsx_pcr *pcr); void rts525a_init_params(struct rtsx_pcr *pcr); void rtl8411b_init_params(struct rtsx_pcr *pcr); +void rts5260_init_params(struct rtsx_pcr *pcr); static inline u8 map_sd_drive(int idx) { @@ -99,5 +102,12 @@ do { \ int rtsx_gops_pm_reset(struct rtsx_pcr *pcr); int rtsx_set_ltr_latency(struct rtsx_pcr *pcr, u32 latency); int rtsx_set_l1off_sub(struct rtsx_pcr *pcr, u8 val); +void rtsx_pci_init_ocp(struct rtsx_pcr *pcr); +void rtsx_pci_disable_ocp(struct rtsx_pcr *pcr); +void rtsx_pci_enable_ocp(struct rtsx_pcr *pcr); +int rtsx_pci_get_ocpstat(struct rtsx_pcr *pcr, u8 *val); +void rtsx_pci_clear_ocpstat(struct rtsx_pcr *pcr); +int rtsx_sd_power_off_card3v3(struct rtsx_pcr *pcr); +int rtsx_ms_power_off_card3v3(struct rtsx_pcr *pcr); #endif diff --git a/drivers/mfd/rtsx_usb.c b/drivers/misc/cardreader/rtsx_usb.c index 59d61b04c197..b97903ff1a72 100644 --- a/drivers/mfd/rtsx_usb.c +++ b/drivers/misc/cardreader/rtsx_usb.c @@ -23,7 +23,7 @@ #include <linux/usb.h> #include <linux/platform_device.h> #include <linux/mfd/core.h> -#include <linux/mfd/rtsx_usb.h> +#include <linux/rtsx_usb.h> static int polling_pipe = 1; module_param(polling_pipe, int, S_IRUGO | S_IWUSR); diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 567028c9219a..cec8152b5ede 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -838,14 +838,14 @@ config MMC_USDHI6ROL0 config MMC_REALTEK_PCI tristate "Realtek PCI-E SD/MMC Card Interface Driver" - depends on MFD_RTSX_PCI + depends on MISC_RTSX_PCI help Say Y here to include driver code to support SD/MMC card interface of Realtek PCI-E card reader config MMC_REALTEK_USB tristate "Realtek USB SD/MMC Card Interface Driver" - depends on MFD_RTSX_USB + depends on MISC_RTSX_USB help Say Y here to include driver code to support SD/MMC card interface of Realtek RTS5129/39 series card reader diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 0848dc0f882e..30bd8081307e 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -30,7 +30,7 @@ #include <linux/mmc/sd.h> #include <linux/mmc/sdio.h> #include <linux/mmc/card.h> -#include <linux/mfd/rtsx_pci.h> +#include <linux/rtsx_pci.h> #include <asm/unaligned.h> struct realtek_pci_sdmmc { diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index 76da1687ab37..78422079ecfa 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -31,7 +31,7 @@ #include <linux/scatterlist.h> #include <linux/pm_runtime.h> -#include <linux/mfd/rtsx_usb.h> +#include <linux/rtsx_usb.h> #include <asm/unaligned.h> #if defined(CONFIG_LEDS_CLASS) || (defined(CONFIG_LEDS_CLASS_MODULE) && \ diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 0ad6e290bbda..e728a96cabfd 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -38,14 +38,8 @@ config CHROMEOS_PSTORE If you have a supported Chromebook, choose Y or M here. The module will be called chromeos_pstore. -config CROS_EC_CHARDEV - tristate "Chrome OS Embedded Controller userspace device interface" - depends on MFD_CROS_EC - ---help--- - This driver adds support to talk with the ChromeOS EC from userspace. - - If you have a supported Chromebook, choose Y or M here. - The module will be called cros_ec_dev. +config CROS_EC_CTL + tristate config CROS_EC_LPC tristate "ChromeOS Embedded Controller (LPC)" diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index a077b1f0211d..ff3b369911f0 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -2,10 +2,9 @@ obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o -cros_ec_devs-objs := cros_ec_dev.o cros_ec_sysfs.o \ - cros_ec_lightbar.o cros_ec_vbc.o \ - cros_ec_debugfs.o -obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_devs.o +cros_ec_ctl-objs := cros_ec_sysfs.o cros_ec_lightbar.o \ + cros_ec_vbc.o cros_ec_debugfs.o +obj-$(CONFIG_CROS_EC_CTL) += cros_ec_ctl.o cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_reg.o cros_ec_lpcs-$(CONFIG_CROS_EC_LPC_MEC) += cros_ec_lpc_mec.o obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c index 4cc66f405760..98a35d32f9dd 100644 --- a/drivers/platform/chrome/cros_ec_debugfs.c +++ b/drivers/platform/chrome/cros_ec_debugfs.c @@ -29,9 +29,6 @@ #include <linux/slab.h> #include <linux/wait.h> -#include "cros_ec_dev.h" -#include "cros_ec_debugfs.h" - #define LOG_SHIFT 14 #define LOG_SIZE (1 << LOG_SHIFT) #define LOG_POLL_SEC 10 @@ -390,6 +387,7 @@ remove_debugfs: debugfs_remove_recursive(debug_info->dir); return ret; } +EXPORT_SYMBOL(cros_ec_debugfs_init); void cros_ec_debugfs_remove(struct cros_ec_dev *ec) { @@ -399,3 +397,4 @@ void cros_ec_debugfs_remove(struct cros_ec_dev *ec) debugfs_remove_recursive(ec->debug_info->dir); cros_ec_cleanup_console_log(ec->debug_info); } +EXPORT_SYMBOL(cros_ec_debugfs_remove); diff --git a/drivers/platform/chrome/cros_ec_debugfs.h b/drivers/platform/chrome/cros_ec_debugfs.h deleted file mode 100644 index 1ff3a50aa1b8..000000000000 --- a/drivers/platform/chrome/cros_ec_debugfs.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2015 Google, 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 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. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef _DRV_CROS_EC_DEBUGFS_H_ -#define _DRV_CROS_EC_DEBUGFS_H_ - -#include "cros_ec_dev.h" - -/* debugfs stuff */ -int cros_ec_debugfs_init(struct cros_ec_dev *ec); -void cros_ec_debugfs_remove(struct cros_ec_dev *ec); - -#endif /* _DRV_CROS_EC_DEBUGFS_H_ */ diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index fd2b047a2748..6ea79d495aa2 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -33,8 +33,6 @@ #include <linux/uaccess.h> #include <linux/slab.h> -#include "cros_ec_dev.h" - /* Rate-limit the lightbar interface to prevent DoS. */ static unsigned long lb_interval_jiffies = 50 * HZ / 1000; @@ -414,6 +412,7 @@ error: return ret; } +EXPORT_SYMBOL(lb_manual_suspend_ctrl); int lb_suspend(struct cros_ec_dev *ec) { @@ -422,6 +421,7 @@ int lb_suspend(struct cros_ec_dev *ec) return lb_send_empty_cmd(ec, LIGHTBAR_CMD_SUSPEND); } +EXPORT_SYMBOL(lb_suspend); int lb_resume(struct cros_ec_dev *ec) { @@ -430,6 +430,7 @@ int lb_resume(struct cros_ec_dev *ec) return lb_send_empty_cmd(ec, LIGHTBAR_CMD_RESUME); } +EXPORT_SYMBOL(lb_resume); static ssize_t sequence_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -622,3 +623,4 @@ struct attribute_group cros_ec_lightbar_attr_group = { .attrs = __lb_cmds_attrs, .is_visible = cros_ec_lightbar_attrs_are_visible, }; +EXPORT_SYMBOL(cros_ec_lightbar_attr_group); diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c index f3baf9973989..d6eebe872187 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -34,8 +34,6 @@ #include <linux/types.h> #include <linux/uaccess.h> -#include "cros_ec_dev.h" - /* Accessor functions */ static ssize_t show_ec_reboot(struct device *dev, @@ -294,4 +292,7 @@ static struct attribute *__ec_attrs[] = { struct attribute_group cros_ec_attr_group = { .attrs = __ec_attrs, }; +EXPORT_SYMBOL(cros_ec_attr_group); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ChromeOS EC control driver"); diff --git a/drivers/platform/chrome/cros_ec_vbc.c b/drivers/platform/chrome/cros_ec_vbc.c index 564a0d08c8bf..6d38e6b08334 100644 --- a/drivers/platform/chrome/cros_ec_vbc.c +++ b/drivers/platform/chrome/cros_ec_vbc.c @@ -135,3 +135,4 @@ struct attribute_group cros_ec_vbc_attr_group = { .bin_attrs = cros_ec_vbc_bin_attrs, .is_bin_visible = cros_ec_vbc_is_visible, }; +EXPORT_SYMBOL(cros_ec_vbc_attr_group); diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index 1bef39828ca7..28133dbd2808 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -132,6 +132,33 @@ void serdev_device_close(struct serdev_device *serdev) } EXPORT_SYMBOL_GPL(serdev_device_close); +static void devm_serdev_device_release(struct device *dev, void *dr) +{ + serdev_device_close(*(struct serdev_device **)dr); +} + +int devm_serdev_device_open(struct device *dev, struct serdev_device *serdev) +{ + struct serdev_device **dr; + int ret; + + dr = devres_alloc(devm_serdev_device_release, sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + ret = serdev_device_open(serdev); + if (ret) { + devres_free(dr); + return ret; + } + + *dr = serdev; + devres_add(dev, dr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_serdev_device_open); + void serdev_device_write_wakeup(struct serdev_device *serdev) { complete(&serdev->write_comp); @@ -268,8 +295,8 @@ static int serdev_drv_probe(struct device *dev) static int serdev_drv_remove(struct device *dev) { const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver); - - sdrv->remove(to_serdev_device(dev)); + if (sdrv->remove) + sdrv->remove(to_serdev_device(dev)); return 0; } diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ca200d1f310a..5bf613d3b7d6 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -223,6 +223,13 @@ config ZIIRAVE_WATCHDOG To compile this driver as a module, choose M here: the module will be called ziirave_wdt. +config RAVE_SP_WATCHDOG + tristate "RAVE SP Watchdog timer" + depends on RAVE_SP_CORE + select WATCHDOG_CORE + help + Support for the watchdog on RAVE SP device. + # ALPHA Architecture # ARM Architecture diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 715a21078e0c..135c5e81f25e 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -224,3 +224,4 @@ obj-$(CONFIG_MAX77620_WATCHDOG) += max77620_wdt.o obj-$(CONFIG_ZIIRAVE_WATCHDOG) += ziirave_wdt.o obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o +obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o diff --git a/drivers/watchdog/rave-sp-wdt.c b/drivers/watchdog/rave-sp-wdt.c new file mode 100644 index 000000000000..35db173252f9 --- /dev/null +++ b/drivers/watchdog/rave-sp-wdt.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Driver for watchdog aspect of for Zodiac Inflight Innovations RAVE + * Supervisory Processor(SP) MCU + * + * Copyright (C) 2017 Zodiac Inflight Innovation + * + */ + +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/mfd/rave-sp.h> +#include <linux/module.h> +#include <linux/nvmem-consumer.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/slab.h> +#include <linux/watchdog.h> + +enum { + RAVE_SP_RESET_BYTE = 1, + RAVE_SP_RESET_REASON_NORMAL = 0, + RAVE_SP_RESET_DELAY_MS = 500, +}; + +/** + * struct rave_sp_wdt_variant - RAVE SP watchdog variant + * + * @max_timeout: Largest possible watchdog timeout setting + * @min_timeout: Smallest possible watchdog timeout setting + * + * @configure: Function to send configuration command + * @restart: Function to send "restart" command + */ +struct rave_sp_wdt_variant { + unsigned int max_timeout; + unsigned int min_timeout; + + int (*configure)(struct watchdog_device *, bool); + int (*restart)(struct watchdog_device *); +}; + +/** + * struct rave_sp_wdt - RAVE SP watchdog + * + * @wdd: Underlying watchdog device + * @sp: Pointer to parent RAVE SP device + * @variant: Device specific variant information + * @reboot_notifier: Reboot notifier implementing machine reset + */ +struct rave_sp_wdt { + struct watchdog_device wdd; + struct rave_sp *sp; + const struct rave_sp_wdt_variant *variant; + struct notifier_block reboot_notifier; +}; + +static struct rave_sp_wdt *to_rave_sp_wdt(struct watchdog_device *wdd) +{ + return container_of(wdd, struct rave_sp_wdt, wdd); +} + +static int rave_sp_wdt_exec(struct watchdog_device *wdd, void *data, + size_t data_size) +{ + return rave_sp_exec(to_rave_sp_wdt(wdd)->sp, + data, data_size, NULL, 0); +} + +static int rave_sp_wdt_legacy_configure(struct watchdog_device *wdd, bool on) +{ + u8 cmd[] = { + [0] = RAVE_SP_CMD_SW_WDT, + [1] = 0, + [2] = 0, + [3] = on, + [4] = on ? wdd->timeout : 0, + }; + + return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd)); +} + +static int rave_sp_wdt_rdu_configure(struct watchdog_device *wdd, bool on) +{ + u8 cmd[] = { + [0] = RAVE_SP_CMD_SW_WDT, + [1] = 0, + [2] = on, + [3] = (u8)wdd->timeout, + [4] = (u8)(wdd->timeout >> 8), + }; + + return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd)); +} + +/** + * rave_sp_wdt_configure - Configure watchdog device + * + * @wdd: Device to configure + * @on: Desired state of the watchdog timer (ON/OFF) + * + * This function configures two aspects of the watchdog timer: + * + * - Wheither it is ON or OFF + * - Its timeout duration + * + * with first aspect specified via function argument and second via + * the value of 'wdd->timeout'. + */ +static int rave_sp_wdt_configure(struct watchdog_device *wdd, bool on) +{ + return to_rave_sp_wdt(wdd)->variant->configure(wdd, on); +} + +static int rave_sp_wdt_legacy_restart(struct watchdog_device *wdd) +{ + u8 cmd[] = { + [0] = RAVE_SP_CMD_RESET, + [1] = 0, + [2] = RAVE_SP_RESET_BYTE + }; + + return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd)); +} + +static int rave_sp_wdt_rdu_restart(struct watchdog_device *wdd) +{ + u8 cmd[] = { + [0] = RAVE_SP_CMD_RESET, + [1] = 0, + [2] = RAVE_SP_RESET_BYTE, + [3] = RAVE_SP_RESET_REASON_NORMAL + }; + + return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd)); +} + +static int rave_sp_wdt_reboot_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + /* + * Restart handler is called in atomic context which means we + * can't communicate to SP via UART. Luckily for use SP will + * wait 500ms before actually resetting us, so we ask it to do + * so here and let the rest of the system go on wrapping + * things up. + */ + if (action == SYS_DOWN || action == SYS_HALT) { + struct rave_sp_wdt *sp_wd = + container_of(nb, struct rave_sp_wdt, reboot_notifier); + + const int ret = sp_wd->variant->restart(&sp_wd->wdd); + + if (ret < 0) + dev_err(sp_wd->wdd.parent, + "Failed to issue restart command (%d)", ret); + return NOTIFY_OK; + } + + return NOTIFY_DONE; +} + +static int rave_sp_wdt_restart(struct watchdog_device *wdd, + unsigned long action, void *data) +{ + /* + * The actual work was done by reboot notifier above. SP + * firmware waits 500 ms before issuing reset, so let's hang + * here for twice that delay and hopefuly we'd never reach + * the return statement. + */ + mdelay(2 * RAVE_SP_RESET_DELAY_MS); + + return -EIO; +} + +static int rave_sp_wdt_start(struct watchdog_device *wdd) +{ + int ret; + + ret = rave_sp_wdt_configure(wdd, true); + if (!ret) + set_bit(WDOG_HW_RUNNING, &wdd->status); + + return ret; +} + +static int rave_sp_wdt_stop(struct watchdog_device *wdd) +{ + return rave_sp_wdt_configure(wdd, false); +} + +static int rave_sp_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + wdd->timeout = timeout; + + return rave_sp_wdt_configure(wdd, watchdog_active(wdd)); +} + +static int rave_sp_wdt_ping(struct watchdog_device *wdd) +{ + u8 cmd[] = { + [0] = RAVE_SP_CMD_PET_WDT, + [1] = 0, + }; + + return rave_sp_wdt_exec(wdd, cmd, sizeof(cmd)); +} + +static const struct watchdog_info rave_sp_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "RAVE SP Watchdog", +}; + +static const struct watchdog_ops rave_sp_wdt_ops = { + .owner = THIS_MODULE, + .start = rave_sp_wdt_start, + .stop = rave_sp_wdt_stop, + .ping = rave_sp_wdt_ping, + .set_timeout = rave_sp_wdt_set_timeout, + .restart = rave_sp_wdt_restart, +}; + +static const struct rave_sp_wdt_variant rave_sp_wdt_legacy = { + .max_timeout = 255, + .min_timeout = 1, + .configure = rave_sp_wdt_legacy_configure, + .restart = rave_sp_wdt_legacy_restart, +}; + +static const struct rave_sp_wdt_variant rave_sp_wdt_rdu = { + .max_timeout = 180, + .min_timeout = 60, + .configure = rave_sp_wdt_rdu_configure, + .restart = rave_sp_wdt_rdu_restart, +}; + +static const struct of_device_id rave_sp_wdt_of_match[] = { + { + .compatible = "zii,rave-sp-watchdog-legacy", + .data = &rave_sp_wdt_legacy, + }, + { + .compatible = "zii,rave-sp-watchdog", + .data = &rave_sp_wdt_rdu, + }, + { /* sentinel */ } +}; + +static int rave_sp_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct watchdog_device *wdd; + struct rave_sp_wdt *sp_wd; + struct nvmem_cell *cell; + __le16 timeout = 0; + int ret; + + sp_wd = devm_kzalloc(dev, sizeof(*sp_wd), GFP_KERNEL); + if (!sp_wd) + return -ENOMEM; + + sp_wd->variant = of_device_get_match_data(dev); + sp_wd->sp = dev_get_drvdata(dev->parent); + + wdd = &sp_wd->wdd; + wdd->parent = dev; + wdd->info = &rave_sp_wdt_info; + wdd->ops = &rave_sp_wdt_ops; + wdd->min_timeout = sp_wd->variant->min_timeout; + wdd->max_timeout = sp_wd->variant->max_timeout; + wdd->status = WATCHDOG_NOWAYOUT_INIT_STATUS; + wdd->timeout = 60; + + cell = nvmem_cell_get(dev, "wdt-timeout"); + if (!IS_ERR(cell)) { + size_t len; + void *value = nvmem_cell_read(cell, &len); + + if (!IS_ERR(value)) { + memcpy(&timeout, value, min(len, sizeof(timeout))); + kfree(value); + } + nvmem_cell_put(cell); + } + watchdog_init_timeout(wdd, le16_to_cpu(timeout), dev); + watchdog_set_restart_priority(wdd, 255); + watchdog_stop_on_unregister(wdd); + + sp_wd->reboot_notifier.notifier_call = rave_sp_wdt_reboot_notifier; + ret = devm_register_reboot_notifier(dev, &sp_wd->reboot_notifier); + if (ret) { + dev_err(dev, "Failed to register reboot notifier\n"); + return ret; + } + + /* + * We don't know if watchdog is running now. To be sure, let's + * start it and depend on watchdog core to ping it + */ + wdd->max_hw_heartbeat_ms = wdd->max_timeout * 1000; + ret = rave_sp_wdt_start(wdd); + if (ret) { + dev_err(dev, "Watchdog didn't start\n"); + return ret; + } + + ret = devm_watchdog_register_device(dev, wdd); + if (ret) { + dev_err(dev, "Failed to register watchdog device\n"); + rave_sp_wdt_stop(wdd); + return ret; + } + + return 0; +} + +static struct platform_driver rave_sp_wdt_driver = { + .probe = rave_sp_wdt_probe, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = rave_sp_wdt_of_match, + }, +}; + +module_platform_driver(rave_sp_wdt_driver); + +MODULE_DEVICE_TABLE(of, rave_sp_wdt_of_match); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andrey Vostrikov <andrey.vostrikov@cogentembedded.com>"); +MODULE_AUTHOR("Nikita Yushchenko <nikita.yoush@cogentembedded.com>"); +MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>"); +MODULE_DESCRIPTION("RAVE SP Watchdog driver"); +MODULE_ALIAS("platform:rave-sp-watchdog"); |