From 585083c539ca3f5fb3d00057b25f9be3304d54c6 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 8 Jul 2016 22:33:37 +0800 Subject: mfd: ac100: Add driver for X-Powers AC100 audio codec / RTC combo IC The AC100 is a multifunction device with an audio codec subsystem and an RTC subsystem. These two subsystems share a common register space and host interface. Signed-off-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- include/linux/mfd/ac100.h | 178 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 include/linux/mfd/ac100.h (limited to 'include/linux/mfd') diff --git a/include/linux/mfd/ac100.h b/include/linux/mfd/ac100.h new file mode 100644 index 000000000000..3c148f196b9f --- /dev/null +++ b/include/linux/mfd/ac100.h @@ -0,0 +1,178 @@ +/* + * Functions and registers to access AC100 codec / RTC combo IC. + * + * Copyright (C) 2016 Chen-Yu Tsai + * + * Chen-Yu Tsai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_MFD_AC100_H +#define __LINUX_MFD_AC100_H + +#include + +struct ac100_dev { + struct device *dev; + struct regmap *regmap; +}; + +/* Audio codec related registers */ +#define AC100_CHIP_AUDIO_RST 0x00 +#define AC100_PLL_CTRL1 0x01 +#define AC100_PLL_CTRL2 0x02 +#define AC100_SYSCLK_CTRL 0x03 +#define AC100_MOD_CLK_ENA 0x04 +#define AC100_MOD_RST_CTRL 0x05 +#define AC100_I2S_SR_CTRL 0x06 + +/* I2S1 interface */ +#define AC100_I2S1_CLK_CTRL 0x10 +#define AC100_I2S1_SND_OUT_CTRL 0x11 +#define AC100_I2S1_SND_IN_CTRL 0x12 +#define AC100_I2S1_MXR_SRC 0x13 +#define AC100_I2S1_VOL_CTRL1 0x14 +#define AC100_I2S1_VOL_CTRL2 0x15 +#define AC100_I2S1_VOL_CTRL3 0x16 +#define AC100_I2S1_VOL_CTRL4 0x17 +#define AC100_I2S1_MXR_GAIN 0x18 + +/* I2S2 interface */ +#define AC100_I2S2_CLK_CTRL 0x20 +#define AC100_I2S2_SND_OUT_CTRL 0x21 +#define AC100_I2S2_SND_IN_CTRL 0x22 +#define AC100_I2S2_MXR_SRC 0x23 +#define AC100_I2S2_VOL_CTRL1 0x24 +#define AC100_I2S2_VOL_CTRL2 0x25 +#define AC100_I2S2_VOL_CTRL3 0x26 +#define AC100_I2S2_VOL_CTRL4 0x27 +#define AC100_I2S2_MXR_GAIN 0x28 + +/* I2S3 interface */ +#define AC100_I2S3_CLK_CTRL 0x30 +#define AC100_I2S3_SND_OUT_CTRL 0x31 +#define AC100_I2S3_SND_IN_CTRL 0x32 +#define AC100_I2S3_SIG_PATH_CTRL 0x33 + +/* ADC digital controls */ +#define AC100_ADC_DIG_CTRL 0x40 +#define AC100_ADC_VOL_CTRL 0x41 + +/* HMIC plug sensing / key detection */ +#define AC100_HMIC_CTRL1 0x44 +#define AC100_HMIC_CTRL2 0x45 +#define AC100_HMIC_STATUS 0x46 + +/* DAC digital controls */ +#define AC100_DAC_DIG_CTRL 0x48 +#define AC100_DAC_VOL_CTRL 0x49 +#define AC100_DAC_MXR_SRC 0x4c +#define AC100_DAC_MXR_GAIN 0x4d + +/* Analog controls */ +#define AC100_ADC_APC_CTRL 0x50 +#define AC100_ADC_SRC 0x51 +#define AC100_ADC_SRC_BST_CTRL 0x52 +#define AC100_OUT_MXR_DAC_A_CTRL 0x53 +#define AC100_OUT_MXR_SRC 0x54 +#define AC100_OUT_MXR_SRC_BST 0x55 +#define AC100_HPOUT_CTRL 0x56 +#define AC100_ERPOUT_CTRL 0x57 +#define AC100_SPKOUT_CTRL 0x58 +#define AC100_LINEOUT_CTRL 0x59 + +/* ADC digital audio processing (high pass filter & auto gain control */ +#define AC100_ADC_DAP_L_STA 0x80 +#define AC100_ADC_DAP_R_STA 0x81 +#define AC100_ADC_DAP_L_CTRL 0x82 +#define AC100_ADC_DAP_R_CTRL 0x83 +#define AC100_ADC_DAP_L_T_L 0x84 /* Left Target Level */ +#define AC100_ADC_DAP_R_T_L 0x85 /* Right Target Level */ +#define AC100_ADC_DAP_L_H_A_C 0x86 /* Left High Avg. Coef */ +#define AC100_ADC_DAP_L_L_A_C 0x87 /* Left Low Avg. Coef */ +#define AC100_ADC_DAP_R_H_A_C 0x88 /* Right High Avg. Coef */ +#define AC100_ADC_DAP_R_L_A_C 0x89 /* Right Low Avg. Coef */ +#define AC100_ADC_DAP_L_D_T 0x8a /* Left Decay Time */ +#define AC100_ADC_DAP_L_A_T 0x8b /* Left Attack Time */ +#define AC100_ADC_DAP_R_D_T 0x8c /* Right Decay Time */ +#define AC100_ADC_DAP_R_A_T 0x8d /* Right Attack Time */ +#define AC100_ADC_DAP_N_TH 0x8e /* Noise Threshold */ +#define AC100_ADC_DAP_L_H_N_A_C 0x8f /* Left High Noise Avg. Coef */ +#define AC100_ADC_DAP_L_L_N_A_C 0x90 /* Left Low Noise Avg. Coef */ +#define AC100_ADC_DAP_R_H_N_A_C 0x91 /* Right High Noise Avg. Coef */ +#define AC100_ADC_DAP_R_L_N_A_C 0x92 /* Right Low Noise Avg. Coef */ +#define AC100_ADC_DAP_H_HPF_C 0x93 /* High High-Pass-Filter Coef */ +#define AC100_ADC_DAP_L_HPF_C 0x94 /* Low High-Pass-Filter Coef */ +#define AC100_ADC_DAP_OPT 0x95 /* AGC Optimum */ + +/* DAC digital audio processing (high pass filter & dynamic range control) */ +#define AC100_DAC_DAP_CTRL 0xa0 +#define AC100_DAC_DAP_H_HPF_C 0xa1 /* High High-Pass-Filter Coef */ +#define AC100_DAC_DAP_L_HPF_C 0xa2 /* Low High-Pass-Filter Coef */ +#define AC100_DAC_DAP_L_H_E_A_C 0xa3 /* Left High Energy Avg Coef */ +#define AC100_DAC_DAP_L_L_E_A_C 0xa4 /* Left Low Energy Avg Coef */ +#define AC100_DAC_DAP_R_H_E_A_C 0xa5 /* Right High Energy Avg Coef */ +#define AC100_DAC_DAP_R_L_E_A_C 0xa6 /* Right Low Energy Avg Coef */ +#define AC100_DAC_DAP_H_G_D_T_C 0xa7 /* High Gain Delay Time Coef */ +#define AC100_DAC_DAP_L_G_D_T_C 0xa8 /* Low Gain Delay Time Coef */ +#define AC100_DAC_DAP_H_G_A_T_C 0xa9 /* High Gain Attack Time Coef */ +#define AC100_DAC_DAP_L_G_A_T_C 0xaa /* Low Gain Attack Time Coef */ +#define AC100_DAC_DAP_H_E_TH 0xab /* High Energy Threshold */ +#define AC100_DAC_DAP_L_E_TH 0xac /* Low Energy Threshold */ +#define AC100_DAC_DAP_H_G_K 0xad /* High Gain K parameter */ +#define AC100_DAC_DAP_L_G_K 0xae /* Low Gain K parameter */ +#define AC100_DAC_DAP_H_G_OFF 0xaf /* High Gain offset */ +#define AC100_DAC_DAP_L_G_OFF 0xb0 /* Low Gain offset */ +#define AC100_DAC_DAP_OPT 0xb1 /* DRC optimum */ + +/* Digital audio processing enable */ +#define AC100_ADC_DAP_ENA 0xb4 +#define AC100_DAC_DAP_ENA 0xb5 + +/* SRC control */ +#define AC100_SRC1_CTRL1 0xb8 +#define AC100_SRC1_CTRL2 0xb9 +#define AC100_SRC1_CTRL3 0xba +#define AC100_SRC1_CTRL4 0xbb +#define AC100_SRC2_CTRL1 0xbc +#define AC100_SRC2_CTRL2 0xbd +#define AC100_SRC2_CTRL3 0xbe +#define AC100_SRC2_CTRL4 0xbf + +/* RTC clk control */ +#define AC100_CLK32K_ANALOG_CTRL 0xc0 +#define AC100_CLKOUT_CTRL1 0xc1 +#define AC100_CLKOUT_CTRL2 0xc2 +#define AC100_CLKOUT_CTRL3 0xc3 + +/* RTC module */ +#define AC100_RTC_RST 0xc6 +#define AC100_RTC_CTRL 0xc7 +#define AC100_RTC_SEC 0xc8 /* second */ +#define AC100_RTC_MIN 0xc9 /* minute */ +#define AC100_RTC_HOU 0xca /* hour */ +#define AC100_RTC_WEE 0xcb /* weekday */ +#define AC100_RTC_DAY 0xcc /* day */ +#define AC100_RTC_MON 0xcd /* month */ +#define AC100_RTC_YEA 0xce /* year */ +#define AC100_RTC_UPD 0xcf /* update trigger */ + +/* RTC alarm */ +#define AC100_ALM_INT_ENA 0xd0 +#define AC100_ALM_INT_STA 0xd1 +#define AC100_ALM_SEC 0xd8 +#define AC100_ALM_MIN 0xd9 +#define AC100_ALM_HOU 0xda +#define AC100_ALM_WEE 0xdb +#define AC100_ALM_DAY 0xdc +#define AC100_ALM_MON 0xdd +#define AC100_ALM_YEA 0xde +#define AC100_ALM_UPD 0xdf + +/* RTC general purpose register 0 ~ 15 */ +#define AC100_RTC_GP(x) (0xe0 + (x)) + +#endif /* __LINUX_MFD_AC100_H */ -- cgit v1.2.3 From 6f1d912b687d3d17c1731f5bda3b5d6703bce4a0 Mon Sep 17 00:00:00 2001 From: Vic Yang Date: Wed, 10 Aug 2016 19:05:24 +0200 Subject: mfd: cros_ec: Add MKBP event support Newer revisions of the ChromeOS EC add more events besides the keyboard ones. So handle interrupts in the MFD driver and let consumers register for notifications for the events they might care. To keep backward compatibility, if the EC doesn't support MKBP event, we fall back to the old MKBP key matrix host command. Cc: Randall Spangler Cc: Vincent Palatin Cc: Benson Leung Signed-off-by: Vic Yang Signed-off-by: Tomeu Vizoso Tested-by: Enric Balletbo i Serra Acked-by: Olof Johansson Signed-off-by: Lee Jones --- drivers/mfd/cros_ec.c | 58 +++++++++++++++++++-- drivers/platform/chrome/cros_ec_proto.c | 92 +++++++++++++++++++++++++++++++++ include/linux/mfd/cros_ec.h | 18 +++++++ include/linux/mfd/cros_ec_commands.h | 34 ++++++++++++ 4 files changed, 199 insertions(+), 3 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 0eee63542038..abd83424b498 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -23,6 +23,7 @@ #include #include #include +#include #define CROS_EC_DEV_EC_INDEX 0 #define CROS_EC_DEV_PD_INDEX 1 @@ -49,11 +50,28 @@ static const struct mfd_cell ec_pd_cell = { .pdata_size = sizeof(pd_p), }; +static irqreturn_t ec_irq_thread(int irq, void *data) +{ + struct cros_ec_device *ec_dev = data; + int ret; + + if (device_may_wakeup(ec_dev->dev)) + pm_wakeup_event(ec_dev->dev, 0); + + ret = cros_ec_get_next_event(ec_dev); + if (ret > 0) + blocking_notifier_call_chain(&ec_dev->event_notifier, + 0, ec_dev); + return IRQ_HANDLED; +} + int cros_ec_register(struct cros_ec_device *ec_dev) { struct device *dev = ec_dev->dev; int err = 0; + BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier); + ec_dev->max_request = sizeof(struct ec_params_hello); ec_dev->max_response = sizeof(struct ec_response_get_protocol_info); ec_dev->max_passthru = 0; @@ -70,13 +88,24 @@ int cros_ec_register(struct cros_ec_device *ec_dev) cros_ec_query_all(ec_dev); + if (ec_dev->irq) { + err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "chromeos-ec", ec_dev); + if (err) { + dev_err(dev, "Failed to request IRQ %d: %d", + ec_dev->irq, err); + return err; + } + } + err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell, 1, NULL, ec_dev->irq, NULL); if (err) { dev_err(dev, "Failed to register Embedded Controller subdevice %d\n", err); - return err; + goto fail_mfd; } if (ec_dev->max_passthru) { @@ -94,7 +123,7 @@ int cros_ec_register(struct cros_ec_device *ec_dev) dev_err(dev, "Failed to register Power Delivery subdevice %d\n", err); - return err; + goto fail_mfd; } } @@ -103,13 +132,18 @@ int cros_ec_register(struct cros_ec_device *ec_dev) if (err) { mfd_remove_devices(dev); dev_err(dev, "Failed to register sub-devices\n"); - return err; + goto fail_mfd; } } dev_info(dev, "Chrome EC device registered\n"); return 0; + +fail_mfd: + if (ec_dev->irq) + free_irq(ec_dev->irq, ec_dev); + return err; } EXPORT_SYMBOL(cros_ec_register); @@ -136,13 +170,31 @@ int cros_ec_suspend(struct cros_ec_device *ec_dev) } EXPORT_SYMBOL(cros_ec_suspend); +static void cros_ec_drain_events(struct cros_ec_device *ec_dev) +{ + while (cros_ec_get_next_event(ec_dev) > 0) + blocking_notifier_call_chain(&ec_dev->event_notifier, + 1, ec_dev); +} + int cros_ec_resume(struct cros_ec_device *ec_dev) { enable_irq(ec_dev->irq); + /* + * In some cases, we need to distinguish between events that occur + * during suspend if the EC is not a wake source. For example, + * keypresses during suspend should be discarded if it does not wake + * the system. + * + * If the EC is not a wake source, drain the event queue and mark them + * as "queued during suspend". + */ if (ec_dev->wake_enabled) { disable_irq_wake(ec_dev->irq); ec_dev->wake_enabled = 0; + } else { + cros_ec_drain_events(ec_dev); } return 0; diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c index 6c084b266651..04053fe1e980 100644 --- a/drivers/platform/chrome/cros_ec_proto.c +++ b/drivers/platform/chrome/cros_ec_proto.c @@ -19,6 +19,7 @@ #include #include #include +#include #define EC_COMMAND_RETRIES 50 @@ -234,11 +235,44 @@ static int cros_ec_host_command_proto_query_v2(struct cros_ec_device *ec_dev) return ret; } +static int cros_ec_get_host_command_version_mask(struct cros_ec_device *ec_dev, + u16 cmd, u32 *mask) +{ + struct ec_params_get_cmd_versions *pver; + struct ec_response_get_cmd_versions *rver; + struct cros_ec_command *msg; + int ret; + + msg = kmalloc(sizeof(*msg) + max(sizeof(*rver), sizeof(*pver)), + GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->version = 0; + msg->command = EC_CMD_GET_CMD_VERSIONS; + msg->insize = sizeof(*rver); + msg->outsize = sizeof(*pver); + + pver = (struct ec_params_get_cmd_versions *)msg->data; + pver->cmd = cmd; + + ret = cros_ec_cmd_xfer(ec_dev, msg); + if (ret > 0) { + rver = (struct ec_response_get_cmd_versions *)msg->data; + *mask = rver->version_mask; + } + + kfree(msg); + + return ret; +} + int cros_ec_query_all(struct cros_ec_device *ec_dev) { struct device *dev = ec_dev->dev; struct cros_ec_command *proto_msg; struct ec_response_get_protocol_info *proto_info; + u32 ver_mask = 0; int ret; proto_msg = kzalloc(sizeof(*proto_msg) + sizeof(*proto_info), @@ -328,6 +362,15 @@ int cros_ec_query_all(struct cros_ec_device *ec_dev) goto exit; } + /* Probe if MKBP event is supported */ + ret = cros_ec_get_host_command_version_mask(ec_dev, + EC_CMD_GET_NEXT_EVENT, + &ver_mask); + if (ret < 0 || ver_mask == 0) + ec_dev->mkbp_event_supported = 0; + else + ec_dev->mkbp_event_supported = 1; + exit: kfree(proto_msg); return ret; @@ -397,3 +440,52 @@ int cros_ec_cmd_xfer_status(struct cros_ec_device *ec_dev, return ret; } EXPORT_SYMBOL(cros_ec_cmd_xfer_status); + +static int get_next_event(struct cros_ec_device *ec_dev) +{ + u8 buffer[sizeof(struct cros_ec_command) + sizeof(ec_dev->event_data)]; + struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; + int ret; + + msg->version = 0; + msg->command = EC_CMD_GET_NEXT_EVENT; + msg->insize = sizeof(ec_dev->event_data); + msg->outsize = 0; + + ret = cros_ec_cmd_xfer(ec_dev, msg); + if (ret > 0) { + ec_dev->event_size = ret - 1; + memcpy(&ec_dev->event_data, msg->data, + sizeof(ec_dev->event_data)); + } + + return ret; +} + +static int get_keyboard_state_event(struct cros_ec_device *ec_dev) +{ + u8 buffer[sizeof(struct cros_ec_command) + + sizeof(ec_dev->event_data.data)]; + struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; + + msg->version = 0; + msg->command = EC_CMD_MKBP_STATE; + msg->insize = sizeof(ec_dev->event_data.data); + msg->outsize = 0; + + ec_dev->event_size = cros_ec_cmd_xfer(ec_dev, msg); + ec_dev->event_data.event_type = EC_MKBP_EVENT_KEY_MATRIX; + memcpy(&ec_dev->event_data.data, msg->data, + sizeof(ec_dev->event_data.data)); + + return ec_dev->event_size; +} + +int cros_ec_get_next_event(struct cros_ec_device *ec_dev) +{ + if (ec_dev->mkbp_event_supported) + return get_next_event(ec_dev); + else + return get_keyboard_state_event(ec_dev); +} +EXPORT_SYMBOL(cros_ec_get_next_event); diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h index d641a18abacb..76f7ef4d3a0d 100644 --- a/include/linux/mfd/cros_ec.h +++ b/include/linux/mfd/cros_ec.h @@ -109,6 +109,10 @@ struct cros_ec_command { * should check msg.result for the EC's result code. * @pkt_xfer: send packet to EC and get response * @lock: one transaction at a time + * @mkbp_event_supported: true if this EC supports the MKBP event protocol. + * @event_notifier: interrupt event notifier for transport devices. + * @event_data: raw payload transferred with the MKBP event. + * @event_size: size in bytes of the event data. */ struct cros_ec_device { @@ -137,6 +141,11 @@ struct cros_ec_device { int (*pkt_xfer)(struct cros_ec_device *ec, struct cros_ec_command *msg); struct mutex lock; + bool mkbp_event_supported; + struct blocking_notifier_head event_notifier; + + struct ec_response_get_next_event event_data; + int event_size; }; /* struct cros_ec_platform - ChromeOS EC platform information @@ -269,6 +278,15 @@ int cros_ec_register(struct cros_ec_device *ec_dev); */ int cros_ec_query_all(struct cros_ec_device *ec_dev); +/** + * cros_ec_get_next_event - Fetch next event from the ChromeOS EC + * + * @ec_dev: Device to fetch event from + * + * Returns: 0 on success, Linux error number on failure + */ +int cros_ec_get_next_event(struct cros_ec_device *ec_dev); + /* sysfs stuff */ extern struct attribute_group cros_ec_attr_group; extern struct attribute_group cros_ec_lightbar_attr_group; diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h index 7e7a8d4b4551..76728ff37d01 100644 --- a/include/linux/mfd/cros_ec_commands.h +++ b/include/linux/mfd/cros_ec_commands.h @@ -1793,6 +1793,40 @@ struct ec_result_keyscan_seq_ctrl { }; } __packed; +/* + * Command for retrieving the next pending MKBP event from the EC device + * + * The device replies with UNAVAILABLE if there aren't any pending events. + */ +#define EC_CMD_GET_NEXT_EVENT 0x67 + +enum ec_mkbp_event { + /* Keyboard matrix changed. The event data is the new matrix state. */ + EC_MKBP_EVENT_KEY_MATRIX = 0, + + /* New host event. The event data is 4 bytes of host event flags. */ + EC_MKBP_EVENT_HOST_EVENT = 1, + + /* New Sensor FIFO data. The event data is fifo_info structure. */ + EC_MKBP_EVENT_SENSOR_FIFO = 2, + + /* Number of MKBP events */ + EC_MKBP_EVENT_COUNT, +}; + +union ec_response_get_next_data { + uint8_t key_matrix[13]; + + /* Unaligned */ + uint32_t host_event; +} __packed; + +struct ec_response_get_next_event { + uint8_t event_type; + /* Followed by event data if any */ + union ec_response_get_next_data data; +} __packed; + /*****************************************************************************/ /* Temperature sensor commands */ -- cgit v1.2.3 From 2eedcbfc0612c87e22c6325fde49ecf140e5873a Mon Sep 17 00:00:00 2001 From: Wadim Egorov Date: Mon, 29 Aug 2016 13:07:58 +0200 Subject: mfd: rk808: Add RK818 support The RK818 chip is a Power Management IC (PMIC) for multimedia and handheld devices. It contains the following components: - Regulators - RTC - Clocking - Battery support Both RK808 and RK818 chips are using a similar register map, so we can reuse the RTC and Clocking functionality. Signed-off-by: Wadim Egorov Tested-by: Andy Yan Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 4 +- drivers/mfd/rk808.c | 226 +++++++++++++++++++++++++++++++++++++++------- include/linux/mfd/rk808.h | 154 +++++++++++++++++++++++++++++-- 3 files changed, 342 insertions(+), 42 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 2d1fb6420592..a55be9520b76 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -852,13 +852,13 @@ config MFD_RC5T583 different functionality of the device. config MFD_RK808 - tristate "Rockchip RK808 Power Management chip" + tristate "Rockchip RK808/RK818 Power Management Chip" depends on I2C && OF select MFD_CORE select REGMAP_I2C select REGMAP_IRQ help - If you say yes here you get support for the RK808 + If you say yes here you get support for the RK808 and RK818 Power Management chips. This driver provides common support for accessing the device through I2C interface. The device supports multiple sub-devices diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c index 49d7f624fc94..0f8acc5882a4 100644 --- a/drivers/mfd/rk808.c +++ b/drivers/mfd/rk808.c @@ -1,11 +1,15 @@ /* - * MFD core driver for Rockchip RK808 + * MFD core driver for Rockchip RK808/RK818 * * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd * * Author: Chris Zhong * Author: Zhang Qing * + * Copyright (C) 2016 PHYTEC Messtechnik GmbH + * + * Author: Wadim Egorov + * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. @@ -21,6 +25,7 @@ #include #include #include +#include #include struct rk808_reg_data { @@ -57,6 +62,14 @@ static bool rk808_is_volatile_reg(struct device *dev, unsigned int reg) return false; } +static const struct regmap_config rk818_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RK818_USB_CTRL_REG, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = rk808_is_volatile_reg, +}; + static const struct regmap_config rk808_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -79,11 +92,21 @@ static const struct mfd_cell rk808s[] = { { .name = "rk808-rtc", .num_resources = ARRAY_SIZE(rtc_resources), - .resources = &rtc_resources[0], + .resources = rtc_resources, }, }; -static const struct rk808_reg_data pre_init_reg[] = { +static const struct mfd_cell rk818s[] = { + { .name = "rk808-clkout", }, + { .name = "rk808-regulator", }, + { + .name = "rk808-rtc", + .num_resources = ARRAY_SIZE(rtc_resources), + .resources = rtc_resources, + }, +}; + +static const struct rk808_reg_data rk808_pre_init_reg[] = { { RK808_BUCK3_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_150MA }, { RK808_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_200MA }, { RK808_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA }, @@ -94,6 +117,24 @@ static const struct rk808_reg_data pre_init_reg[] = { VB_LO_SEL_3500MV }, }; +static const struct rk808_reg_data rk818_pre_init_reg[] = { + /* improve efficiency */ + { RK818_BUCK2_CONFIG_REG, BUCK2_RATE_MASK, BUCK_ILMIN_250MA }, + { RK818_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_250MA }, + { RK818_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA }, + { RK818_USB_CTRL_REG, RK818_USB_ILIM_SEL_MASK, + RK818_USB_ILMIN_2000MA }, + /* close charger when usb lower then 3.4V */ + { RK818_USB_CTRL_REG, RK818_USB_CHG_SD_VSEL_MASK, + (0x7 << 4) }, + /* no action when vref */ + { RK818_H5V_EN_REG, BIT(1), RK818_REF_RDY_CTRL }, + /* enable HDMI 5V */ + { RK818_H5V_EN_REG, BIT(0), RK818_H5V_EN }, + { RK808_VB_MON_REG, MASK_ALL, VB_LO_ACT | + VB_LO_SEL_3500MV }, +}; + static const struct regmap_irq rk808_irqs[] = { /* INT_STS */ [RK808_IRQ_VOUT_LO] = { @@ -136,6 +177,76 @@ static const struct regmap_irq rk808_irqs[] = { }, }; +static const struct regmap_irq rk818_irqs[] = { + /* INT_STS */ + [RK818_IRQ_VOUT_LO] = { + .mask = RK818_IRQ_VOUT_LO_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_VB_LO] = { + .mask = RK818_IRQ_VB_LO_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_PWRON] = { + .mask = RK818_IRQ_PWRON_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_PWRON_LP] = { + .mask = RK818_IRQ_PWRON_LP_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_HOTDIE] = { + .mask = RK818_IRQ_HOTDIE_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_RTC_ALARM] = { + .mask = RK818_IRQ_RTC_ALARM_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_RTC_PERIOD] = { + .mask = RK818_IRQ_RTC_PERIOD_MSK, + .reg_offset = 0, + }, + [RK818_IRQ_USB_OV] = { + .mask = RK818_IRQ_USB_OV_MSK, + .reg_offset = 0, + }, + + /* INT_STS2 */ + [RK818_IRQ_PLUG_IN] = { + .mask = RK818_IRQ_PLUG_IN_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_PLUG_OUT] = { + .mask = RK818_IRQ_PLUG_OUT_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_CHG_OK] = { + .mask = RK818_IRQ_CHG_OK_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_CHG_TE] = { + .mask = RK818_IRQ_CHG_TE_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_CHG_TS1] = { + .mask = RK818_IRQ_CHG_TS1_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_TS2] = { + .mask = RK818_IRQ_TS2_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_CHG_CVTLIM] = { + .mask = RK818_IRQ_CHG_CVTLIM_MSK, + .reg_offset = 1, + }, + [RK818_IRQ_DISCHG_ILIM] = { + .mask = RK818_IRQ_DISCHG_ILIM_MSK, + .reg_offset = 1, + }, +}; + static struct regmap_irq_chip rk808_irq_chip = { .name = "rk808", .irqs = rk808_irqs, @@ -148,6 +259,18 @@ static struct regmap_irq_chip rk808_irq_chip = { .init_ack_masked = true, }; +static struct regmap_irq_chip rk818_irq_chip = { + .name = "rk818", + .irqs = rk818_irqs, + .num_irqs = ARRAY_SIZE(rk818_irqs), + .num_regs = 2, + .irq_reg_stride = 2, + .status_base = RK818_INT_STS_REG1, + .mask_base = RK818_INT_STS_MSK_REG1, + .ack_base = RK818_INT_STS_REG1, + .init_ack_masked = true, +}; + static struct i2c_client *rk808_i2c_client; static void rk808_device_shutdown(void) { @@ -167,55 +290,100 @@ static void rk808_device_shutdown(void) dev_err(&rk808_i2c_client->dev, "power off error!\n"); } +static const struct of_device_id rk808_of_match[] = { + { .compatible = "rockchip,rk808" }, + { .compatible = "rockchip,rk818" }, + { }, +}; +MODULE_DEVICE_TABLE(of, rk808_of_match); + static int rk808_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device_node *np = client->dev.of_node; struct rk808 *rk808; + const struct rk808_reg_data *pre_init_reg; + const struct mfd_cell *cells; + int nr_pre_init_regs; + int nr_cells; int pm_off = 0; int ret; int i; - if (!client->irq) { - dev_err(&client->dev, "No interrupt support, no core IRQ\n"); - return -EINVAL; - } - rk808 = devm_kzalloc(&client->dev, sizeof(*rk808), GFP_KERNEL); if (!rk808) return -ENOMEM; - rk808->regmap = devm_regmap_init_i2c(client, &rk808_regmap_config); + rk808->variant = i2c_smbus_read_word_data(client, RK808_ID_MSB); + if (rk808->variant < 0) { + dev_err(&client->dev, "Failed to read the chip id at 0x%02x\n", + RK808_ID_MSB); + return rk808->variant; + } + + dev_dbg(&client->dev, "Chip id: 0x%x\n", (unsigned int)rk808->variant); + + switch (rk808->variant) { + case RK808_ID: + rk808->regmap_cfg = &rk808_regmap_config; + rk808->regmap_irq_chip = &rk808_irq_chip; + pre_init_reg = rk808_pre_init_reg; + nr_pre_init_regs = ARRAY_SIZE(rk808_pre_init_reg); + cells = rk808s; + nr_cells = ARRAY_SIZE(rk808s); + break; + case RK818_ID: + rk808->regmap_cfg = &rk818_regmap_config; + rk808->regmap_irq_chip = &rk818_irq_chip; + pre_init_reg = rk818_pre_init_reg; + nr_pre_init_regs = ARRAY_SIZE(rk818_pre_init_reg); + cells = rk818s; + nr_cells = ARRAY_SIZE(rk818s); + break; + default: + dev_err(&client->dev, "Unsupported RK8XX ID %lu\n", + rk808->variant); + return -EINVAL; + } + + rk808->i2c = client; + i2c_set_clientdata(client, rk808); + + rk808->regmap = devm_regmap_init_i2c(client, rk808->regmap_cfg); if (IS_ERR(rk808->regmap)) { dev_err(&client->dev, "regmap initialization failed\n"); return PTR_ERR(rk808->regmap); } - for (i = 0; i < ARRAY_SIZE(pre_init_reg); i++) { - ret = regmap_update_bits(rk808->regmap, pre_init_reg[i].addr, - pre_init_reg[i].mask, - pre_init_reg[i].value); - if (ret) { - dev_err(&client->dev, - "0x%x write err\n", pre_init_reg[i].addr); - return ret; - } + if (!client->irq) { + dev_err(&client->dev, "No interrupt support, no core IRQ\n"); + return -EINVAL; } ret = regmap_add_irq_chip(rk808->regmap, client->irq, IRQF_ONESHOT, -1, - &rk808_irq_chip, &rk808->irq_data); + rk808->regmap_irq_chip, &rk808->irq_data); if (ret) { dev_err(&client->dev, "Failed to add irq_chip %d\n", ret); return ret; } - rk808->i2c = client; - i2c_set_clientdata(client, rk808); + for (i = 0; i < nr_pre_init_regs; i++) { + ret = regmap_update_bits(rk808->regmap, + pre_init_reg[i].addr, + pre_init_reg[i].mask, + pre_init_reg[i].value); + if (ret) { + dev_err(&client->dev, + "0x%x write err\n", + pre_init_reg[i].addr); + return ret; + } + } - ret = devm_mfd_add_devices(&client->dev, -1, - rk808s, ARRAY_SIZE(rk808s), NULL, 0, - regmap_irq_get_domain(rk808->irq_data)); + ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, + cells, nr_cells, NULL, 0, + regmap_irq_get_domain(rk808->irq_data)); if (ret) { dev_err(&client->dev, "failed to add MFD devices %d\n", ret); goto err_irq; @@ -245,14 +413,9 @@ static int rk808_remove(struct i2c_client *client) return 0; } -static const struct of_device_id rk808_of_match[] = { - { .compatible = "rockchip,rk808" }, - { }, -}; -MODULE_DEVICE_TABLE(of, rk808_of_match); - static const struct i2c_device_id rk808_ids[] = { { "rk808" }, + { "rk818" }, { }, }; MODULE_DEVICE_TABLE(i2c, rk808_ids); @@ -272,4 +435,5 @@ module_i2c_driver(rk808_i2c_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Chris Zhong "); MODULE_AUTHOR("Zhang Qing "); -MODULE_DESCRIPTION("RK808 PMIC driver"); +MODULE_AUTHOR("Wadim Egorov "); +MODULE_DESCRIPTION("RK808/RK818 PMIC driver"); diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h index 441b6ee72691..fc5db6fcb57d 100644 --- a/include/linux/mfd/rk808.h +++ b/include/linux/mfd/rk808.h @@ -1,11 +1,15 @@ /* - * rk808.h for Rockchip RK808 + * Register definitions for Rockchip's RK808/RK818 PMIC * * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd * * Author: Chris Zhong * Author: Zhang Qing * + * Copyright (C) 2016 PHYTEC Messtechnik GmbH + * + * Author: Wadim Egorov + * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. @@ -16,8 +20,8 @@ * more details. */ -#ifndef __LINUX_REGULATOR_rk808_H -#define __LINUX_REGULATOR_rk808_H +#ifndef __LINUX_REGULATOR_RK808_H +#define __LINUX_REGULATOR_RK808_H #include #include @@ -28,7 +32,7 @@ #define RK808_DCDC1 0 /* (0+RK808_START) */ #define RK808_LDO1 4 /* (4+RK808_START) */ -#define RK808_NUM_REGULATORS 14 +#define RK808_NUM_REGULATORS 14 enum rk808_reg { RK808_ID_DCDC1, @@ -65,6 +69,8 @@ enum rk808_reg { #define RK808_RTC_INT_REG 0x12 #define RK808_RTC_COMP_LSB_REG 0x13 #define RK808_RTC_COMP_MSB_REG 0x14 +#define RK808_ID_MSB 0x17 +#define RK808_ID_LSB 0x18 #define RK808_CLK32OUT_REG 0x20 #define RK808_VB_MON_REG 0x21 #define RK808_THERMAL_REG 0x22 @@ -115,7 +121,92 @@ enum rk808_reg { #define RK808_INT_STS_MSK_REG2 0x4f #define RK808_IO_POL_REG 0x50 -/* IRQ Definitions */ +/* RK818 */ +#define RK818_DCDC1 0 +#define RK818_LDO1 4 +#define RK818_NUM_REGULATORS 17 + +enum rk818_reg { + RK818_ID_DCDC1, + RK818_ID_DCDC2, + RK818_ID_DCDC3, + RK818_ID_DCDC4, + RK818_ID_BOOST, + RK818_ID_LDO1, + RK818_ID_LDO2, + RK818_ID_LDO3, + RK818_ID_LDO4, + RK818_ID_LDO5, + RK818_ID_LDO6, + RK818_ID_LDO7, + RK818_ID_LDO8, + RK818_ID_LDO9, + RK818_ID_SWITCH, + RK818_ID_HDMI_SWITCH, + RK818_ID_OTG_SWITCH, +}; + +#define RK818_DCDC_EN_REG 0x23 +#define RK818_LDO_EN_REG 0x24 +#define RK818_SLEEP_SET_OFF_REG1 0x25 +#define RK818_SLEEP_SET_OFF_REG2 0x26 +#define RK818_DCDC_UV_STS_REG 0x27 +#define RK818_DCDC_UV_ACT_REG 0x28 +#define RK818_LDO_UV_STS_REG 0x29 +#define RK818_LDO_UV_ACT_REG 0x2a +#define RK818_DCDC_PG_REG 0x2b +#define RK818_LDO_PG_REG 0x2c +#define RK818_VOUT_MON_TDB_REG 0x2d +#define RK818_BUCK1_CONFIG_REG 0x2e +#define RK818_BUCK1_ON_VSEL_REG 0x2f +#define RK818_BUCK1_SLP_VSEL_REG 0x30 +#define RK818_BUCK2_CONFIG_REG 0x32 +#define RK818_BUCK2_ON_VSEL_REG 0x33 +#define RK818_BUCK2_SLP_VSEL_REG 0x34 +#define RK818_BUCK3_CONFIG_REG 0x36 +#define RK818_BUCK4_CONFIG_REG 0x37 +#define RK818_BUCK4_ON_VSEL_REG 0x38 +#define RK818_BUCK4_SLP_VSEL_REG 0x39 +#define RK818_BOOST_CONFIG_REG 0x3a +#define RK818_LDO1_ON_VSEL_REG 0x3b +#define RK818_LDO1_SLP_VSEL_REG 0x3c +#define RK818_LDO2_ON_VSEL_REG 0x3d +#define RK818_LDO2_SLP_VSEL_REG 0x3e +#define RK818_LDO3_ON_VSEL_REG 0x3f +#define RK818_LDO3_SLP_VSEL_REG 0x40 +#define RK818_LDO4_ON_VSEL_REG 0x41 +#define RK818_LDO4_SLP_VSEL_REG 0x42 +#define RK818_LDO5_ON_VSEL_REG 0x43 +#define RK818_LDO5_SLP_VSEL_REG 0x44 +#define RK818_LDO6_ON_VSEL_REG 0x45 +#define RK818_LDO6_SLP_VSEL_REG 0x46 +#define RK818_LDO7_ON_VSEL_REG 0x47 +#define RK818_LDO7_SLP_VSEL_REG 0x48 +#define RK818_LDO8_ON_VSEL_REG 0x49 +#define RK818_LDO8_SLP_VSEL_REG 0x4a +#define RK818_BOOST_LDO9_ON_VSEL_REG 0x54 +#define RK818_BOOST_LDO9_SLP_VSEL_REG 0x55 +#define RK818_DEVCTRL_REG 0x4b +#define RK818_INT_STS_REG1 0X4c +#define RK818_INT_STS_MSK_REG1 0x4d +#define RK818_INT_STS_REG2 0x4e +#define RK818_INT_STS_MSK_REG2 0x4f +#define RK818_IO_POL_REG 0x50 +#define RK818_H5V_EN_REG 0x52 +#define RK818_SLEEP_SET_OFF_REG3 0x53 +#define RK818_BOOST_LDO9_ON_VSEL_REG 0x54 +#define RK818_BOOST_LDO9_SLP_VSEL_REG 0x55 +#define RK818_BOOST_CTRL_REG 0x56 +#define RK818_DCDC_ILMAX 0x90 +#define RK818_USB_CTRL_REG 0xa1 + +#define RK818_H5V_EN BIT(0) +#define RK818_REF_RDY_CTRL BIT(1) +#define RK818_USB_ILIM_SEL_MASK 0xf +#define RK818_USB_ILMIN_2000MA 0x7 +#define RK818_USB_CHG_SD_VSEL_MASK 0x70 + +/* RK808 IRQ Definitions */ #define RK808_IRQ_VOUT_LO 0 #define RK808_IRQ_VB_LO 1 #define RK808_IRQ_PWRON 2 @@ -137,6 +228,43 @@ enum rk808_reg { #define RK808_IRQ_PLUG_IN_INT_MSK BIT(0) #define RK808_IRQ_PLUG_OUT_INT_MSK BIT(1) +/* RK818 IRQ Definitions */ +#define RK818_IRQ_VOUT_LO 0 +#define RK818_IRQ_VB_LO 1 +#define RK818_IRQ_PWRON 2 +#define RK818_IRQ_PWRON_LP 3 +#define RK818_IRQ_HOTDIE 4 +#define RK818_IRQ_RTC_ALARM 5 +#define RK818_IRQ_RTC_PERIOD 6 +#define RK818_IRQ_USB_OV 7 +#define RK818_IRQ_PLUG_IN 8 +#define RK818_IRQ_PLUG_OUT 9 +#define RK818_IRQ_CHG_OK 10 +#define RK818_IRQ_CHG_TE 11 +#define RK818_IRQ_CHG_TS1 12 +#define RK818_IRQ_TS2 13 +#define RK818_IRQ_CHG_CVTLIM 14 +#define RK818_IRQ_DISCHG_ILIM 7 + +#define RK818_IRQ_VOUT_LO_MSK BIT(0) +#define RK818_IRQ_VB_LO_MSK BIT(1) +#define RK818_IRQ_PWRON_MSK BIT(2) +#define RK818_IRQ_PWRON_LP_MSK BIT(3) +#define RK818_IRQ_HOTDIE_MSK BIT(4) +#define RK818_IRQ_RTC_ALARM_MSK BIT(5) +#define RK818_IRQ_RTC_PERIOD_MSK BIT(6) +#define RK818_IRQ_USB_OV_MSK BIT(7) +#define RK818_IRQ_PLUG_IN_MSK BIT(0) +#define RK818_IRQ_PLUG_OUT_MSK BIT(1) +#define RK818_IRQ_CHG_OK_MSK BIT(2) +#define RK818_IRQ_CHG_TE_MSK BIT(3) +#define RK818_IRQ_CHG_TS1_MSK BIT(4) +#define RK818_IRQ_TS2_MSK BIT(5) +#define RK818_IRQ_CHG_CVTLIM_MSK BIT(6) +#define RK818_IRQ_DISCHG_ILIM_MSK BIT(7) + +#define RK818_NUM_IRQ 16 + #define RK808_VBAT_LOW_2V8 0x00 #define RK808_VBAT_LOW_2V9 0x01 #define RK808_VBAT_LOW_3V0 0x02 @@ -191,9 +319,17 @@ enum { BOOST_ILMIN_250MA, }; +enum { + RK808_ID = 0x0000, + RK818_ID = 0x8181, +}; + struct rk808 { - struct i2c_client *i2c; - struct regmap_irq_chip_data *irq_data; - struct regmap *regmap; + struct i2c_client *i2c; + struct regmap_irq_chip_data *irq_data; + struct regmap *regmap; + long variant; + const struct regmap_config *regmap_cfg; + const struct regmap_irq_chip *regmap_irq_chip; }; -#endif /* __LINUX_REGULATOR_rk808_H */ +#endif /* __LINUX_REGULATOR_RK808_H */ -- cgit v1.2.3 From 8824ee8573483e1c91691b5be3d3730e75551dce Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sat, 27 Aug 2016 15:55:38 +0800 Subject: mfd: axp20x: Add support for AXP806 PMIC The X-Powers AXP806 is a new PMIC that is paired with Allwinner's A80 SoC, along with a master AXP809 PMIC. This PMIC has a new register layout, and supports some functions not seen in other X-Powers PMICs, such as master-slave mode, or having multiple AXP806 PMICs on the same bus with address space extension, or supporting both I2C and RSB mode. I2C has not been tested. This patch adds support for the interrupts of the PMIC. A regulator sub-device is enabled, but actual regulator support will come in a later patch. Signed-off-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/mfd/axp20x-rsb.c | 1 + drivers/mfd/axp20x.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/axp20x.h | 60 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+) (limited to 'include/linux/mfd') diff --git a/drivers/mfd/axp20x-rsb.c b/drivers/mfd/axp20x-rsb.c index a407527bcd09..a732cb50bcff 100644 --- a/drivers/mfd/axp20x-rsb.c +++ b/drivers/mfd/axp20x-rsb.c @@ -61,6 +61,7 @@ static int axp20x_rsb_remove(struct sunxi_rsb_device *rdev) static const struct of_device_id axp20x_rsb_of_match[] = { { .compatible = "x-powers,axp223", .data = (void *)AXP223_ID }, + { .compatible = "x-powers,axp806", .data = (void *)AXP806_ID }, { .compatible = "x-powers,axp809", .data = (void *)AXP809_ID }, { }, }; diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index fd80b0981f0f..96102753847f 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -38,6 +38,7 @@ static const char * const axp20x_model_names[] = { "AXP221", "AXP223", "AXP288", + "AXP806", "AXP809", }; @@ -129,6 +130,27 @@ static const struct regmap_access_table axp288_volatile_table = { .n_yes_ranges = ARRAY_SIZE(axp288_volatile_ranges), }; +static const struct regmap_range axp806_writeable_ranges[] = { + regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_DATACACHE(3)), + regmap_reg_range(AXP806_PWR_OUT_CTRL1, AXP806_CLDO3_V_CTRL), + regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ2_EN), + regmap_reg_range(AXP20X_IRQ1_STATE, AXP20X_IRQ2_STATE), +}; + +static const struct regmap_range axp806_volatile_ranges[] = { + regmap_reg_range(AXP20X_IRQ1_STATE, AXP20X_IRQ2_STATE), +}; + +static const struct regmap_access_table axp806_writeable_table = { + .yes_ranges = axp806_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(axp806_writeable_ranges), +}; + +static const struct regmap_access_table axp806_volatile_table = { + .yes_ranges = axp806_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(axp806_volatile_ranges), +}; + static struct resource axp152_pek_resources[] = { DEFINE_RES_IRQ_NAMED(AXP152_IRQ_PEK_RIS_EDGE, "PEK_DBR"), DEFINE_RES_IRQ_NAMED(AXP152_IRQ_PEK_FAL_EDGE, "PEK_DBF"), @@ -278,6 +300,15 @@ static const struct regmap_config axp288_regmap_config = { .cache_type = REGCACHE_RBTREE, }; +static const struct regmap_config axp806_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .wr_table = &axp806_writeable_table, + .volatile_table = &axp806_volatile_table, + .max_register = AXP806_VREF_TEMP_WARN_L, + .cache_type = REGCACHE_RBTREE, +}; + #define INIT_REGMAP_IRQ(_variant, _irq, _off, _mask) \ [_variant##_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) } @@ -409,6 +440,21 @@ static const struct regmap_irq axp288_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP288, BC_USB_CHNG, 5, 1), }; +static const struct regmap_irq axp806_regmap_irqs[] = { + INIT_REGMAP_IRQ(AXP806, DIE_TEMP_HIGH_LV1, 0, 0), + INIT_REGMAP_IRQ(AXP806, DIE_TEMP_HIGH_LV2, 0, 1), + INIT_REGMAP_IRQ(AXP806, DCDCA_V_LOW, 0, 3), + INIT_REGMAP_IRQ(AXP806, DCDCB_V_LOW, 0, 4), + INIT_REGMAP_IRQ(AXP806, DCDCC_V_LOW, 0, 5), + INIT_REGMAP_IRQ(AXP806, DCDCD_V_LOW, 0, 6), + INIT_REGMAP_IRQ(AXP806, DCDCE_V_LOW, 0, 7), + INIT_REGMAP_IRQ(AXP806, PWROK_LONG, 1, 0), + INIT_REGMAP_IRQ(AXP806, PWROK_SHORT, 1, 1), + INIT_REGMAP_IRQ(AXP806, WAKEUP, 1, 4), + INIT_REGMAP_IRQ(AXP806, PWROK_FALL, 1, 5), + INIT_REGMAP_IRQ(AXP806, PWROK_RISE, 1, 6), +}; + static const struct regmap_irq axp809_regmap_irqs[] = { INIT_REGMAP_IRQ(AXP809, ACIN_OVER_V, 0, 7), INIT_REGMAP_IRQ(AXP809, ACIN_PLUGIN, 0, 6), @@ -494,6 +540,18 @@ static const struct regmap_irq_chip axp288_regmap_irq_chip = { }; +static const struct regmap_irq_chip axp806_regmap_irq_chip = { + .name = "axp806", + .status_base = AXP20X_IRQ1_STATE, + .ack_base = AXP20X_IRQ1_STATE, + .mask_base = AXP20X_IRQ1_EN, + .mask_invert = true, + .init_ack_masked = true, + .irqs = axp806_regmap_irqs, + .num_irqs = ARRAY_SIZE(axp806_regmap_irqs), + .num_regs = 2, +}; + static const struct regmap_irq_chip axp809_regmap_irq_chip = { .name = "axp809", .status_base = AXP20X_IRQ1_STATE, @@ -660,12 +718,20 @@ static struct mfd_cell axp288_cells[] = { }, }; +static struct mfd_cell axp806_cells[] = { + { + .id = 2, + .name = "axp20x-regulator", + }, +}; + static struct mfd_cell axp809_cells[] = { { .name = "axp20x-pek", .num_resources = ARRAY_SIZE(axp809_pek_resources), .resources = axp809_pek_resources, }, { + .id = 1, .name = "axp20x-regulator", }, }; @@ -732,6 +798,12 @@ int axp20x_match_device(struct axp20x_dev *axp20x) axp20x->regmap_cfg = &axp288_regmap_config; axp20x->regmap_irq_chip = &axp288_regmap_irq_chip; break; + case AXP806_ID: + axp20x->nr_cells = ARRAY_SIZE(axp806_cells); + axp20x->cells = axp806_cells; + axp20x->regmap_cfg = &axp806_regmap_config; + axp20x->regmap_irq_chip = &axp806_regmap_irq_chip; + break; case AXP809_ID: axp20x->nr_cells = ARRAY_SIZE(axp809_cells); axp20x->cells = axp809_cells; diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index 0be4982f08fe..fec597fb34cb 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h @@ -20,6 +20,7 @@ enum { AXP221_ID, AXP223_ID, AXP288_ID, + AXP806_ID, AXP809_ID, NR_AXP20X_VARIANTS, }; @@ -91,6 +92,30 @@ enum { #define AXP22X_ALDO3_V_OUT 0x2a #define AXP22X_CHRG_CTRL3 0x35 +#define AXP806_STARTUP_SRC 0x00 +#define AXP806_CHIP_ID 0x03 +#define AXP806_PWR_OUT_CTRL1 0x10 +#define AXP806_PWR_OUT_CTRL2 0x11 +#define AXP806_DCDCA_V_CTRL 0x12 +#define AXP806_DCDCB_V_CTRL 0x13 +#define AXP806_DCDCC_V_CTRL 0x14 +#define AXP806_DCDCD_V_CTRL 0x15 +#define AXP806_DCDCE_V_CTRL 0x16 +#define AXP806_ALDO1_V_CTRL 0x17 +#define AXP806_ALDO2_V_CTRL 0x18 +#define AXP806_ALDO3_V_CTRL 0x19 +#define AXP806_DCDC_MODE_CTRL1 0x1a +#define AXP806_DCDC_MODE_CTRL2 0x1b +#define AXP806_DCDC_FREQ_CTRL 0x1c +#define AXP806_BLDO1_V_CTRL 0x20 +#define AXP806_BLDO2_V_CTRL 0x21 +#define AXP806_BLDO3_V_CTRL 0x22 +#define AXP806_BLDO4_V_CTRL 0x23 +#define AXP806_CLDO1_V_CTRL 0x24 +#define AXP806_CLDO2_V_CTRL 0x25 +#define AXP806_CLDO3_V_CTRL 0x26 +#define AXP806_VREF_TEMP_WARN_L 0xf3 + /* Interrupt */ #define AXP152_IRQ1_EN 0x40 #define AXP152_IRQ2_EN 0x41 @@ -265,6 +290,26 @@ enum { AXP22X_REG_ID_MAX, }; +enum { + AXP806_DCDCA = 0, + AXP806_DCDCB, + AXP806_DCDCC, + AXP806_DCDCD, + AXP806_DCDCE, + AXP806_ALDO1, + AXP806_ALDO2, + AXP806_ALDO3, + AXP806_BLDO1, + AXP806_BLDO2, + AXP806_BLDO3, + AXP806_BLDO4, + AXP806_CLDO1, + AXP806_CLDO2, + AXP806_CLDO3, + AXP806_SW, + AXP806_REG_ID_MAX, +}; + enum { AXP809_DCDC1 = 0, AXP809_DCDC2, @@ -414,6 +459,21 @@ enum axp288_irqs { AXP288_IRQ_BC_USB_CHNG, }; +enum axp806_irqs { + AXP806_IRQ_DIE_TEMP_HIGH_LV1, + AXP806_IRQ_DIE_TEMP_HIGH_LV2, + AXP806_IRQ_DCDCA_V_LOW, + AXP806_IRQ_DCDCB_V_LOW, + AXP806_IRQ_DCDCC_V_LOW, + AXP806_IRQ_DCDCD_V_LOW, + AXP806_IRQ_DCDCE_V_LOW, + AXP806_IRQ_PWROK_LONG, + AXP806_IRQ_PWROK_SHORT, + AXP806_IRQ_WAKEUP, + AXP806_IRQ_PWROK_FALL, + AXP806_IRQ_PWROK_RISE, +}; + enum axp809_irqs { AXP809_IRQ_ACIN_OVER_V = 1, AXP809_IRQ_ACIN_PLUGIN, -- cgit v1.2.3 From cdd8da8cc66b3d205120560649e530978ccbc567 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 2 Sep 2016 16:52:46 +0100 Subject: mfd: arizona: Add gating of external MCLKn clocks This patch adds requesting of the clocks supplied on MCLK1, MCLK2 pins, gating of the 32k clock is added to the arizona_clk32k_enable(), arizona_clk32k_disable() helpers. It's a temporary change until the CODEC's clock controller gets exposed through the clk API and is helpful for board configurations where the MCLK clocks are not provided by always on oscillators. Signed-off-by: Sylwester Nawrocki Signed-off-by: Charles Keepax Signed-off-by: Lee Jones --- drivers/mfd/arizona-core.c | 30 ++++++++++++++++++++++++++++-- include/linux/mfd/arizona/core.h | 9 +++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index e4f97b3c824b..4fa0fae51006 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -10,6 +10,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -49,7 +50,15 @@ int arizona_clk32k_enable(struct arizona *arizona) case ARIZONA_32KZ_MCLK1: ret = pm_runtime_get_sync(arizona->dev); if (ret != 0) - goto out; + goto err_ref; + ret = clk_prepare_enable(arizona->mclk[ARIZONA_MCLK1]); + if (ret != 0) + goto err_pm; + break; + case ARIZONA_32KZ_MCLK2: + ret = clk_prepare_enable(arizona->mclk[ARIZONA_MCLK2]); + if (ret != 0) + goto err_ref; break; } @@ -58,7 +67,9 @@ int arizona_clk32k_enable(struct arizona *arizona) ARIZONA_CLK_32K_ENA); } -out: +err_pm: + pm_runtime_put_sync(arizona->dev); +err_ref: if (ret != 0) arizona->clk32k_ref--; @@ -83,6 +94,10 @@ int arizona_clk32k_disable(struct arizona *arizona) switch (arizona->pdata.clk32k_src) { case ARIZONA_32KZ_MCLK1: pm_runtime_put_sync(arizona->dev); + clk_disable_unprepare(arizona->mclk[ARIZONA_MCLK1]); + break; + case ARIZONA_32KZ_MCLK2: + clk_disable_unprepare(arizona->mclk[ARIZONA_MCLK2]); break; } } @@ -1000,6 +1015,7 @@ static const struct mfd_cell wm8998_devs[] = { int arizona_dev_init(struct arizona *arizona) { + const char * const mclk_name[] = { "mclk1", "mclk2" }; struct device *dev = arizona->dev; const char *type_name = NULL; unsigned int reg, val, mask; @@ -1016,6 +1032,16 @@ int arizona_dev_init(struct arizona *arizona) else arizona_of_get_core_pdata(arizona); + BUILD_BUG_ON(ARRAY_SIZE(arizona->mclk) != ARRAY_SIZE(mclk_name)); + for (i = 0; i < ARRAY_SIZE(arizona->mclk); i++) { + arizona->mclk[i] = devm_clk_get(arizona->dev, mclk_name[i]); + if (IS_ERR(arizona->mclk[i])) { + dev_info(arizona->dev, "Failed to get %s: %ld\n", + mclk_name[i], PTR_ERR(arizona->mclk[i])); + arizona->mclk[i] = NULL; + } + } + regcache_cache_only(arizona->regmap, true); switch (arizona->type) { diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 58ab4c0fe761..b9909bb0642b 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -13,6 +13,7 @@ #ifndef _WM_ARIZONA_CORE_H #define _WM_ARIZONA_CORE_H +#include #include #include #include @@ -21,6 +22,12 @@ #define ARIZONA_MAX_CORE_SUPPLIES 2 +enum { + ARIZONA_MCLK1, + ARIZONA_MCLK2, + ARIZONA_NUM_MCLK +}; + enum arizona_type { WM5102 = 1, WM5110 = 2, @@ -139,6 +146,8 @@ struct arizona { struct mutex clk_lock; int clk32k_ref; + struct clk *mclk[ARIZONA_NUM_MCLK]; + bool ctrlif_error; struct snd_soc_dapm_context *dapm; -- cgit v1.2.3 From 37778d83a744a1ebed1478b490601c40f8827607 Mon Sep 17 00:00:00 2001 From: Steve Twiss Date: Mon, 8 Aug 2016 14:16:11 +0100 Subject: mfd: da9063: Update author information to remove incorrect e-mail addresses Remove incorrect e-mail addresses from the copyright header and MODULE_AUTHOR() macro. These e-mail addresses are no longer in use. The author names have not been changed, only the e-mail addresses have been deleted from the source files. Signed-off-by: Steve Twiss Signed-off-by: Lee Jones --- drivers/mfd/da9063-core.c | 7 ++++--- drivers/mfd/da9063-i2c.c | 2 +- drivers/mfd/da9063-irq.c | 2 +- include/linux/mfd/da9063/core.h | 4 ++-- include/linux/mfd/da9063/pdata.h | 4 ++-- include/linux/mfd/da9063/registers.h | 4 ++-- 6 files changed, 12 insertions(+), 11 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/mfd/da9063-core.c b/drivers/mfd/da9063-core.c index af841c165787..6c2870d4e754 100644 --- a/drivers/mfd/da9063-core.c +++ b/drivers/mfd/da9063-core.c @@ -4,8 +4,8 @@ * Copyright 2012 Dialog Semiconductors Ltd. * Copyright 2013 Philipp Zabel, Pengutronix * - * Author: Krystian Garbaciak , - * Michal Hajduk + * Author: Krystian Garbaciak, Dialog Semiconductor + * Author: Michal Hajduk, Dialog Semiconductor * * 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 @@ -242,5 +242,6 @@ void da9063_device_exit(struct da9063 *da9063) } MODULE_DESCRIPTION("PMIC driver for Dialog DA9063"); -MODULE_AUTHOR("Krystian Garbaciak , Michal Hajduk "); +MODULE_AUTHOR("Krystian Garbaciak"); +MODULE_AUTHOR("Michal Hajduk"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/da9063-i2c.c b/drivers/mfd/da9063-i2c.c index 73901084945f..981805a2c521 100644 --- a/drivers/mfd/da9063-i2c.c +++ b/drivers/mfd/da9063-i2c.c @@ -3,7 +3,7 @@ * Copyright 2012 Dialog Semiconductor Ltd. * Copyright 2013 Philipp Zabel, Pengutronix * - * Author: Krystian Garbaciak + * Author: Krystian Garbaciak, Dialog Semiconductor * * 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 diff --git a/drivers/mfd/da9063-irq.c b/drivers/mfd/da9063-irq.c index 7e903fcb8813..207bbfe55449 100644 --- a/drivers/mfd/da9063-irq.c +++ b/drivers/mfd/da9063-irq.c @@ -3,7 +3,7 @@ * Copyright 2012 Dialog Semiconductor Ltd. * Copyright 2013 Philipp Zabel, Pengutronix * - * Author: Michal Hajduk + * Author: Michal Hajduk, Dialog Semiconductor * * 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 diff --git a/include/linux/mfd/da9063/core.h b/include/linux/mfd/da9063/core.h index 621af82123c6..f3ae65db4c86 100644 --- a/include/linux/mfd/da9063/core.h +++ b/include/linux/mfd/da9063/core.h @@ -3,8 +3,8 @@ * * Copyright 2012 Dialog Semiconductor Ltd. * - * Author: Michal Hajduk - * Krystian Garbaciak + * Author: Michal Hajduk, Dialog Semiconductor + * Author: Krystian Garbaciak, Dialog Semiconductor * * 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 diff --git a/include/linux/mfd/da9063/pdata.h b/include/linux/mfd/da9063/pdata.h index 612383bd80ae..8a125701ef7b 100644 --- a/include/linux/mfd/da9063/pdata.h +++ b/include/linux/mfd/da9063/pdata.h @@ -3,8 +3,8 @@ * * Copyright 2012 Dialog Semiconductor Ltd. * - * Author: Michal Hajduk - * Author: Krystian Garbaciak + * Author: Michal Hajduk, Dialog Semiconductor + * Author: Krystian Garbaciak, Dialog Semiconductor * * 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 diff --git a/include/linux/mfd/da9063/registers.h b/include/linux/mfd/da9063/registers.h index 2e0ba6d5fbc3..5d42859cb441 100644 --- a/include/linux/mfd/da9063/registers.h +++ b/include/linux/mfd/da9063/registers.h @@ -3,8 +3,8 @@ * * Copyright 2012 Dialog Semiconductor Ltd. * - * Author: Michal Hajduk - * Krystian Garbaciak + * Author: Michal Hajduk, Dialog Semiconductor + * Author: Krystian Garbaciak, Dialog Semiconductor * * 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 -- cgit v1.2.3 From 9a6dc644512fd083400a96ac4a035ac154fe6b8d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 4 Aug 2016 08:26:56 +0300 Subject: mfd: 88pm80x: Double shifting bug in suspend/resume set_bit() and clear_bit() take the bit number so this code is really doing "1 << (1 << irq)" which is a double shift bug. It's done consistently so it won't cause a problem unless "irq" is more than 4. Fixes: 70c6cce04066 ('mfd: Support 88pm80x in 80x driver') Signed-off-by: Dan Carpenter Signed-off-by: Lee Jones --- include/linux/mfd/88pm80x.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux/mfd') diff --git a/include/linux/mfd/88pm80x.h b/include/linux/mfd/88pm80x.h index d409ceb2231e..c118a7ec94d6 100644 --- a/include/linux/mfd/88pm80x.h +++ b/include/linux/mfd/88pm80x.h @@ -350,7 +350,7 @@ static inline int pm80x_dev_suspend(struct device *dev) int irq = platform_get_irq(pdev, 0); if (device_may_wakeup(dev)) - set_bit((1 << irq), &chip->wu_flag); + set_bit(irq, &chip->wu_flag); return 0; } @@ -362,7 +362,7 @@ static inline int pm80x_dev_resume(struct device *dev) int irq = platform_get_irq(pdev, 0); if (device_may_wakeup(dev)) - clear_bit((1 << irq), &chip->wu_flag); + clear_bit(irq, &chip->wu_flag); return 0; } -- cgit v1.2.3 From 8c5d0571596efa5656cc53144172baa7c5c57b43 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 17 Aug 2016 14:07:42 +0200 Subject: mfd: max14577: Change Krzysztof Kozlowski's email to kernel.org Change my email address to kernel.org instead of Samsung one for the purpose of any future contact. The copyrights remain untouched and are attributed to Samsung. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Lee Jones --- drivers/mfd/max14577.c | 4 ++-- include/linux/mfd/max14577-private.h | 2 +- include/linux/mfd/max14577.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c index 6c245128ab2e..6cbe96b28f42 100644 --- a/drivers/mfd/max14577.c +++ b/drivers/mfd/max14577.c @@ -3,7 +3,7 @@ * * Copyright (C) 2014 Samsung Electronics * Chanwoo Choi - * Krzysztof Kozlowski + * Krzysztof Kozlowski * * 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 @@ -569,6 +569,6 @@ static void __exit max14577_i2c_exit(void) } module_exit(max14577_i2c_exit); -MODULE_AUTHOR("Chanwoo Choi , Krzysztof Kozlowski "); +MODULE_AUTHOR("Chanwoo Choi , Krzysztof Kozlowski "); MODULE_DESCRIPTION("Maxim 14577/77836 multi-function core driver"); MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/max14577-private.h b/include/linux/mfd/max14577-private.h index f01c1fae4d84..df75234f979d 100644 --- a/include/linux/mfd/max14577-private.h +++ b/include/linux/mfd/max14577-private.h @@ -3,7 +3,7 @@ * * Copyright (C) 2014 Samsung Electrnoics * Chanwoo Choi - * Krzysztof Kozlowski + * Krzysztof Kozlowski * * 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 diff --git a/include/linux/mfd/max14577.h b/include/linux/mfd/max14577.h index ccfaf952c31b..d81b52bb8bee 100644 --- a/include/linux/mfd/max14577.h +++ b/include/linux/mfd/max14577.h @@ -3,7 +3,7 @@ * * Copyright (C) 2014 Samsung Electrnoics * Chanwoo Choi - * Krzysztof Kozlowski + * Krzysztof Kozlowski * * 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 -- cgit v1.2.3 From c695abab2429cfa9554aa353702936d1f064f073 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 10 Aug 2016 16:48:20 +0200 Subject: mfd: Add Samsung Exynos Low Power Audio Subsystem driver This patch adds common driver for the Top block of the Samsung Exynos SoC Low Power Audio Subsystem. This is a minimal driver which prepares resources for IP blocks like I2S, audio DMA and UART and exposes a regmap for the Top block registers. Also system power ops are added to ensure the Audio Subsystem is operational after system suspend/resume cycle. Signed-off-by: Inha Song Signed-off-by: Beomho Seo Signed-off-by: Sylwester Nawrocki Tested-by: Chanwoo Choi Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 8 ++ drivers/mfd/Makefile | 1 + drivers/mfd/exynos-lpass.c | 187 +++++++++++++++++++++++++++++++++ include/linux/mfd/syscon/exynos5-pmu.h | 4 +- 4 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 drivers/mfd/exynos-lpass.c (limited to 'include/linux/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 362aa1a172f6..9ac9f6250ba8 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -291,6 +291,14 @@ config MFD_DLN2 etc. must be enabled in order to use the functionality of the device. +config MFD_EXYNOS_LPASS + tristate "Samsung Exynos SoC Low Power Audio Subsystem" + select MFD_CORE + select REGMAP_MMIO + help + Select this option to enable support for Samsung Exynos Low Power + Audio Subsystem. + config MFD_MC13XXX tristate depends on (SPI_MASTER || I2C) diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 5bf35c6a528b..f18dce1e5e3f 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.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 diff --git a/drivers/mfd/exynos-lpass.c b/drivers/mfd/exynos-lpass.c new file mode 100644 index 000000000000..578ac7b57b96 --- /dev/null +++ b/drivers/mfd/exynos-lpass.c @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2015 - 2016 Samsung Electronics Co., Ltd. + * + * Authors: Inha Song + * Sylwester Nawrocki + * + * Samsung Exynos SoC series Low Power Audio Subsystem driver. + * + * This module provides regmap for the Top SFR region and instantiates + * devices for IP blocks like DMAC, I2S, UART. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* LPASS Top register definitions */ +#define SFR_LPASS_CORE_SW_RESET 0x08 +#define LPASS_SB_SW_RESET BIT(11) +#define LPASS_UART_SW_RESET BIT(10) +#define LPASS_PCM_SW_RESET BIT(9) +#define LPASS_I2S_SW_RESET BIT(8) +#define LPASS_WDT1_SW_RESET BIT(4) +#define LPASS_WDT0_SW_RESET BIT(3) +#define LPASS_TIMER_SW_RESET BIT(2) +#define LPASS_MEM_SW_RESET BIT(1) +#define LPASS_DMA_SW_RESET BIT(0) + +#define SFR_LPASS_INTR_CA5_MASK 0x48 +#define SFR_LPASS_INTR_CPU_MASK 0x58 +#define LPASS_INTR_APM BIT(9) +#define LPASS_INTR_MIF BIT(8) +#define LPASS_INTR_TIMER BIT(7) +#define LPASS_INTR_DMA BIT(6) +#define LPASS_INTR_GPIO BIT(5) +#define LPASS_INTR_I2S BIT(4) +#define LPASS_INTR_PCM BIT(3) +#define LPASS_INTR_SLIMBUS BIT(2) +#define LPASS_INTR_UART BIT(1) +#define LPASS_INTR_SFR BIT(0) + +struct exynos_lpass { + /* pointer to the Power Management Unit regmap */ + struct regmap *pmu; + /* pointer to the LPASS TOP regmap */ + struct regmap *top; +}; + +static void exynos_lpass_core_sw_reset(struct exynos_lpass *lpass, int mask) +{ + unsigned int val = 0; + + regmap_read(lpass->top, SFR_LPASS_CORE_SW_RESET, &val); + + val &= ~mask; + regmap_write(lpass->top, SFR_LPASS_CORE_SW_RESET, val); + + usleep_range(100, 150); + + val |= mask; + regmap_write(lpass->top, SFR_LPASS_CORE_SW_RESET, val); +} + +static void exynos_lpass_enable(struct exynos_lpass *lpass) +{ + /* Unmask SFR, DMA and I2S interrupt */ + regmap_write(lpass->top, SFR_LPASS_INTR_CA5_MASK, + LPASS_INTR_SFR | LPASS_INTR_DMA | LPASS_INTR_I2S); + + regmap_write(lpass->top, SFR_LPASS_INTR_CPU_MASK, + LPASS_INTR_SFR | LPASS_INTR_DMA | LPASS_INTR_I2S); + + /* Activate related PADs from retention state */ + regmap_write(lpass->pmu, EXYNOS5433_PAD_RETENTION_AUD_OPTION, + EXYNOS5433_PAD_INITIATE_WAKEUP_FROM_LOWPWR); + + exynos_lpass_core_sw_reset(lpass, LPASS_I2S_SW_RESET); + exynos_lpass_core_sw_reset(lpass, LPASS_DMA_SW_RESET); + exynos_lpass_core_sw_reset(lpass, LPASS_MEM_SW_RESET); +} + +static void exynos_lpass_disable(struct exynos_lpass *lpass) +{ + /* Mask any unmasked IP interrupt sources */ + regmap_write(lpass->top, SFR_LPASS_INTR_CPU_MASK, 0); + regmap_write(lpass->top, SFR_LPASS_INTR_CA5_MASK, 0); + + /* Deactivate related PADs from retention state */ + regmap_write(lpass->pmu, EXYNOS5433_PAD_RETENTION_AUD_OPTION, 0); +} + +static const struct regmap_config exynos_lpass_reg_conf = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xfc, + .fast_io = true, +}; + +static int exynos_lpass_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct exynos_lpass *lpass; + void __iomem *base_top; + struct resource *res; + + lpass = devm_kzalloc(dev, sizeof(*lpass), GFP_KERNEL); + if (!lpass) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base_top = devm_ioremap_resource(dev, res); + if (IS_ERR(base_top)) + return PTR_ERR(base_top); + + lpass->top = regmap_init_mmio(dev, base_top, + &exynos_lpass_reg_conf); + if (IS_ERR(lpass->top)) { + dev_err(dev, "LPASS top regmap initialization failed\n"); + return PTR_ERR(lpass->top); + } + + lpass->pmu = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,pmu-syscon"); + if (IS_ERR(lpass->pmu)) { + dev_err(dev, "Failed to lookup PMU regmap\n"); + return PTR_ERR(lpass->pmu); + } + + platform_set_drvdata(pdev, lpass); + exynos_lpass_enable(lpass); + + return of_platform_populate(dev->of_node, NULL, NULL, dev); +} + +#ifdef CONFIG_PM_SLEEP +static int exynos_lpass_suspend(struct device *dev) +{ + struct exynos_lpass *lpass = dev_get_drvdata(dev); + + exynos_lpass_disable(lpass); + + return 0; +} + +static int exynos_lpass_resume(struct device *dev) +{ + struct exynos_lpass *lpass = dev_get_drvdata(dev); + + exynos_lpass_enable(lpass); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(lpass_pm_ops, exynos_lpass_suspend, + exynos_lpass_resume); + +static const struct of_device_id exynos_lpass_of_match[] = { + { .compatible = "samsung,exynos5433-lpass" }, + { }, +}; +MODULE_DEVICE_TABLE(of, exynos_lpass_of_match); + +static struct platform_driver exynos_lpass_driver = { + .driver = { + .name = "exynos-lpass", + .pm = &lpass_pm_ops, + .of_match_table = exynos_lpass_of_match, + }, + .probe = exynos_lpass_probe, +}; +module_platform_driver(exynos_lpass_driver); + +MODULE_DESCRIPTION("Samsung Low Power Audio Subsystem driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/syscon/exynos5-pmu.h b/include/linux/mfd/syscon/exynos5-pmu.h index 76f30f940c70..c28ff21ca4d2 100644 --- a/include/linux/mfd/syscon/exynos5-pmu.h +++ b/include/linux/mfd/syscon/exynos5-pmu.h @@ -43,8 +43,10 @@ #define EXYNOS5433_MIPI_PHY2_CONTROL (0x718) #define EXYNOS5_PHY_ENABLE BIT(0) - #define EXYNOS5_MIPI_PHY_S_RESETN BIT(1) #define EXYNOS5_MIPI_PHY_M_RESETN BIT(2) +#define EXYNOS5433_PAD_RETENTION_AUD_OPTION (0x3028) +#define EXYNOS5433_PAD_INITIATE_WAKEUP_FROM_LOWPWR BIT(28) + #endif /* _LINUX_MFD_SYSCON_PMU_EXYNOS5_H_ */ -- cgit v1.2.3 From 6556bdacf646fcaa0586123ba85412de1c8f0eee Mon Sep 17 00:00:00 2001 From: Marcin Niestroj Date: Fri, 9 Sep 2016 10:42:02 +0200 Subject: mfd: tps65217: Add support for IRQs Add support for handling IRQs: power button, AC and USB power state changes. Mask and interrupt bits are shared within one register, which prevents us to use regmap_irq implementation. New irq_domain is created in order to add interrupt handling for each tps65217's subsystem. IRQ resources have been added for charger subsystem to be able to notify about AC and USB state changes. Signed-off-by: Marcin Niestroj Reviewed-by: Grygorii Strashko Tested-by: Tony Lindgren Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 1 + drivers/mfd/tps65217.c | 195 +++++++++++++++++++++++++++++++++++++++++-- include/linux/mfd/tps65217.h | 12 +++ 3 files changed, 200 insertions(+), 8 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9ac9f6250ba8..a23ef437572b 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1232,6 +1232,7 @@ config MFD_TPS65217 depends on I2C select MFD_CORE select REGMAP_I2C + select IRQ_DOMAIN help If you say yes here you get support for the TPS65217 series of Power Management / White LED chips. diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index 049a6fcac651..41b5d598814d 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -15,22 +15,99 @@ * GNU General Public License for more details. */ -#include #include -#include -#include +#include #include +#include #include -#include -#include -#include +#include +#include +#include +#include #include #include +#include +#include +#include #include #include -static const struct mfd_cell tps65217s[] = { +static struct resource charger_resources[] = { + DEFINE_RES_IRQ_NAMED(TPS65217_IRQ_AC, "AC"), + DEFINE_RES_IRQ_NAMED(TPS65217_IRQ_USB, "USB"), +}; + +struct tps65217_irq { + int mask; + int interrupt; +}; + +static const struct tps65217_irq tps65217_irqs[] = { + [TPS65217_IRQ_PB] = { + .mask = TPS65217_INT_PBM, + .interrupt = TPS65217_INT_PBI, + }, + [TPS65217_IRQ_AC] = { + .mask = TPS65217_INT_ACM, + .interrupt = TPS65217_INT_ACI, + }, + [TPS65217_IRQ_USB] = { + .mask = TPS65217_INT_USBM, + .interrupt = TPS65217_INT_USBI, + }, +}; + +static void tps65217_irq_lock(struct irq_data *data) +{ + struct tps65217 *tps = irq_data_get_irq_chip_data(data); + + mutex_lock(&tps->irq_lock); +} + +static void tps65217_irq_sync_unlock(struct irq_data *data) +{ + struct tps65217 *tps = irq_data_get_irq_chip_data(data); + int ret; + + ret = tps65217_reg_write(tps, TPS65217_REG_INT, tps->irq_mask, + TPS65217_PROTECT_NONE); + if (ret != 0) + dev_err(tps->dev, "Failed to sync IRQ masks\n"); + + mutex_unlock(&tps->irq_lock); +} + +static const inline struct tps65217_irq * +irq_to_tps65217_irq(struct tps65217 *tps, struct irq_data *data) +{ + return &tps65217_irqs[data->hwirq]; +} + +static void tps65217_irq_enable(struct irq_data *data) +{ + struct tps65217 *tps = irq_data_get_irq_chip_data(data); + const struct tps65217_irq *irq_data = irq_to_tps65217_irq(tps, data); + + tps->irq_mask &= ~irq_data->mask; +} + +static void tps65217_irq_disable(struct irq_data *data) +{ + struct tps65217 *tps = irq_data_get_irq_chip_data(data); + const struct tps65217_irq *irq_data = irq_to_tps65217_irq(tps, data); + + tps->irq_mask |= irq_data->mask; +} + +static struct irq_chip tps65217_irq_chip = { + .irq_bus_lock = tps65217_irq_lock, + .irq_bus_sync_unlock = tps65217_irq_sync_unlock, + .irq_enable = tps65217_irq_enable, + .irq_disable = tps65217_irq_disable, +}; + +static struct mfd_cell tps65217s[] = { { .name = "tps65217-pmic", .of_compatible = "ti,tps65217-pmic", @@ -41,10 +118,90 @@ static const struct mfd_cell tps65217s[] = { }, { .name = "tps65217-charger", + .num_resources = ARRAY_SIZE(charger_resources), + .resources = charger_resources, .of_compatible = "ti,tps65217-charger", }, }; +static irqreturn_t tps65217_irq_thread(int irq, void *data) +{ + struct tps65217 *tps = data; + unsigned int status; + bool handled = false; + int i; + int ret; + + ret = tps65217_reg_read(tps, TPS65217_REG_INT, &status); + if (ret < 0) { + dev_err(tps->dev, "Failed to read IRQ status: %d\n", + ret); + return IRQ_NONE; + } + + for (i = 0; i < ARRAY_SIZE(tps65217_irqs); i++) { + if (status & tps65217_irqs[i].interrupt) { + handle_nested_irq(irq_find_mapping(tps->irq_domain, i)); + handled = true; + } + } + + if (handled) + return IRQ_HANDLED; + + return IRQ_NONE; +} + +static int tps65217_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct tps65217 *tps = h->host_data; + + irq_set_chip_data(virq, tps); + irq_set_chip_and_handler(virq, &tps65217_irq_chip, handle_edge_irq); + irq_set_nested_thread(virq, 1); + irq_set_parent(virq, tps->irq); + irq_set_noprobe(virq); + + return 0; +} + +static const struct irq_domain_ops tps65217_irq_domain_ops = { + .map = tps65217_irq_map, +}; + +static int tps65217_irq_init(struct tps65217 *tps, int irq) +{ + int ret; + + mutex_init(&tps->irq_lock); + tps->irq = irq; + + /* Mask all interrupt sources */ + tps->irq_mask = (TPS65217_INT_RESERVEDM | TPS65217_INT_PBM + | TPS65217_INT_ACM | TPS65217_INT_USBM); + tps65217_reg_write(tps, TPS65217_REG_INT, tps->irq_mask, + TPS65217_PROTECT_NONE); + + tps->irq_domain = irq_domain_add_linear(tps->dev->of_node, + TPS65217_NUM_IRQ, &tps65217_irq_domain_ops, tps); + if (!tps->irq_domain) { + dev_err(tps->dev, "Could not create IRQ domain\n"); + return -ENOMEM; + } + + ret = devm_request_threaded_irq(tps->dev, irq, NULL, + tps65217_irq_thread, IRQF_ONESHOT, + "tps65217-irq", tps); + if (ret) { + dev_err(tps->dev, "Failed to request IRQ %d: %d\n", + irq, ret); + return ret; + } + + return 0; +} + /** * tps65217_reg_read: Read a single tps65217 register. * @@ -149,11 +306,22 @@ int tps65217_clear_bits(struct tps65217 *tps, unsigned int reg, } EXPORT_SYMBOL_GPL(tps65217_clear_bits); +static bool tps65217_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TPS65217_REG_INT: + return true; + default: + return false; + } +} + static const struct regmap_config tps65217_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = TPS65217_REG_MAX, + .volatile_reg = tps65217_volatile_reg, }; static const struct of_device_id tps65217_of_match[] = { @@ -205,8 +373,19 @@ static int tps65217_probe(struct i2c_client *client, return ret; } + if (client->irq) { + tps65217_irq_init(tps, client->irq); + } else { + int i; + + /* Don't tell children about IRQ resources which won't fire */ + for (i = 0; i < ARRAY_SIZE(tps65217s); i++) + tps65217s[i].num_resources = 0; + } + ret = devm_mfd_add_devices(tps->dev, -1, tps65217s, - ARRAY_SIZE(tps65217s), NULL, 0, NULL); + ARRAY_SIZE(tps65217s), NULL, 0, + tps->irq_domain); if (ret < 0) { dev_err(tps->dev, "mfd_add_devices failed: %d\n", ret); return ret; diff --git a/include/linux/mfd/tps65217.h b/include/linux/mfd/tps65217.h index 1c88231496d3..4ccda8969639 100644 --- a/include/linux/mfd/tps65217.h +++ b/include/linux/mfd/tps65217.h @@ -73,6 +73,7 @@ #define TPS65217_PPATH_AC_CURRENT_MASK 0x0C #define TPS65217_PPATH_USB_CURRENT_MASK 0x03 +#define TPS65217_INT_RESERVEDM BIT(7) #define TPS65217_INT_PBM BIT(6) #define TPS65217_INT_ACM BIT(5) #define TPS65217_INT_USBM BIT(4) @@ -233,6 +234,13 @@ struct tps65217_bl_pdata { int dft_brightness; }; +enum tps65217_irq_type { + TPS65217_IRQ_PB, + TPS65217_IRQ_AC, + TPS65217_IRQ_USB, + TPS65217_NUM_IRQ +}; + /** * struct tps65217_board - packages regulator init data * @tps65217_regulator_data: regulator initialization values @@ -258,6 +266,10 @@ struct tps65217 { struct regulator_desc desc[TPS65217_NUM_REGULATOR]; struct regmap *regmap; u8 *strobes; + struct irq_domain *irq_domain; + struct mutex irq_lock; + u8 irq_mask; + int irq; }; static inline struct tps65217 *dev_to_tps65217(struct device *dev) -- cgit v1.2.3 From fe62c477df0c9501dea8c16d78e722c902737a53 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 6 Sep 2016 08:59:55 +0800 Subject: mfd: lp873x: Remove unused mutex lock from struct lp873x The mutex is not used, so remove it. Signed-off-by: Axel Lin Signed-off-by: Lee Jones --- drivers/mfd/lp873x.c | 2 -- include/linux/mfd/lp873x.h | 1 - 2 files changed, 3 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/mfd/lp873x.c b/drivers/mfd/lp873x.c index 9af064c940ee..873c608e6a5d 100644 --- a/drivers/mfd/lp873x.c +++ b/drivers/mfd/lp873x.c @@ -53,8 +53,6 @@ static int lp873x_probe(struct i2c_client *client, return ret; } - mutex_init(&lp873->lock); - ret = regmap_read(lp873->regmap, LP873X_REG_OTP_REV, &otpid); if (ret) { dev_err(lp873->dev, "Failed to read OTP ID\n"); diff --git a/include/linux/mfd/lp873x.h b/include/linux/mfd/lp873x.h index 83b1bd7588be..edbec8350a49 100644 --- a/include/linux/mfd/lp873x.h +++ b/include/linux/mfd/lp873x.h @@ -263,7 +263,6 @@ enum lp873x_regulator_id { struct lp873x { struct device *dev; u8 rev; - struct mutex lock; /* lock guarding the data structure */ struct regmap *regmap; }; #endif /* __LINUX_MFD_LP873X_H */ -- cgit v1.2.3 From fae5e033d65a03c8b705be8ffc0ef69162544368 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 6 Sep 2016 15:13:01 +0200 Subject: mfd: rk808: Fix RK818_IRQ_DISCHG_ILIM initializer When building with -Woverride-init, we get a warning about an incorrect initializer: drivers/mfd/rk808.c:244:8: error: initialized field overwritten [-Werror=override-init] [RK818_IRQ_DISCHG_ILIM] = { This is clearly a mistake, as both RK818_IRQ_DISCHG_ILIM and RK818_IRQ_USB_OV are defined as '7', but they refer to different register bits. Changing RK818_IRQ_DISCHG_ILIM to 15 is consistent with how all other 14 interrupts are handled here, so I'm assuming this is what it should have been. Fixes: 2eedcbfc0612 ("mfd: rk808: Add RK818 support") Signed-off-by: Arnd Bergmann Acked-by: Andy Yan Signed-off-by: Lee Jones --- include/linux/mfd/rk808.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux/mfd') diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h index fc5db6fcb57d..6d435a3c06bc 100644 --- a/include/linux/mfd/rk808.h +++ b/include/linux/mfd/rk808.h @@ -244,7 +244,7 @@ enum rk818_reg { #define RK818_IRQ_CHG_TS1 12 #define RK818_IRQ_TS2 13 #define RK818_IRQ_CHG_CVTLIM 14 -#define RK818_IRQ_DISCHG_ILIM 7 +#define RK818_IRQ_DISCHG_ILIM 15 #define RK818_IRQ_VOUT_LO_MSK BIT(0) #define RK818_IRQ_VB_LO_MSK BIT(1) -- cgit v1.2.3 From c45eab2cb06092aa61e67fc6801cf0d6fac29482 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 14 Sep 2016 11:14:30 +0100 Subject: mfd: ab8500-debugfs: Prevent initialised field from being over-written Due to the lack of parity in the way array fields have been named/ numbered, a mistake was made where more debug fields were declared than actually existed. In doing so, 2 fields were added, which although unclear, were already declared in the array. The result was that the latter declarations trashed the former ones. This patch places the array back in the correct order and removes the offending NULL entries. While we're at it, let's ensure this doesn't happen again by naming each field properly and add a new *_LAST define to describe how many fields there should be. Signed-off-by: Lee Jones --- drivers/mfd/ab8500-debugfs.c | 50 ++++++++++++++++----------------------- include/linux/mfd/abx500/ab8500.h | 2 ++ 2 files changed, 23 insertions(+), 29 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 0aecd7bd35f8..6c913c41f976 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -153,14 +153,14 @@ static struct hwreg_cfg hwreg_cfg = { #define AB8500_NAME_STRING "ab8500" #define AB8500_ADC_NAME_STRING "gpadc" -#define AB8500_NUM_BANKS 24 +#define AB8500_NUM_BANKS AB8500_DEBUG_FIELD_LAST #define AB8500_REV_REG 0x80 static struct ab8500_prcmu_ranges *debug_ranges; static struct ab8500_prcmu_ranges ab8500_debug_ranges[AB8500_NUM_BANKS] = { - [0x0] = { + [AB8500_M_FSM_RANK] = { .num_ranges = 0, .range = NULL, }, @@ -315,7 +315,7 @@ static struct ab8500_prcmu_ranges ab8500_debug_ranges[AB8500_NUM_BANKS] = { }, }, }, - [0x9] = { + [AB8500_RESERVED] = { .num_ranges = 0, .range = NULL, }, @@ -386,24 +386,6 @@ static struct ab8500_prcmu_ranges ab8500_debug_ranges[AB8500_NUM_BANKS] = { }, }, }, - [AB8500_DEVELOPMENT] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - }, - }, - [AB8500_DEBUG] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x05, - .last = 0x07, - }, - }, - }, [AB8500_AUDIO] = { .num_ranges = 1, .range = (struct ab8500_reg_range[]) { @@ -463,19 +445,29 @@ static struct ab8500_prcmu_ranges ab8500_debug_ranges[AB8500_NUM_BANKS] = { }, }, }, - [0x11] = { - .num_ranges = 0, - .range = NULL, + [AB8500_DEVELOPMENT] = { + .num_ranges = 1, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x00, + .last = 0x00, + }, + }, }, - [0x12] = { - .num_ranges = 0, - .range = NULL, + [AB8500_DEBUG] = { + .num_ranges = 1, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x05, + .last = 0x07, + }, + }, }, - [0x13] = { + [AB8500_PROD_TEST] = { .num_ranges = 0, .range = NULL, }, - [0x14] = { + [AB8500_STE_TEST] = { .num_ranges = 0, .range = NULL, }, diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 9475fee2bfc5..d33c245e75ca 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -63,6 +63,8 @@ enum ab8500_version { #define AB8500_STE_TEST 0x14 #define AB8500_OTP_EMUL 0x15 +#define AB8500_DEBUG_FIELD_LAST 0x16 + /* * Interrupts * Values used to index into array ab8500_irq_regoffset[] defined in -- cgit v1.2.3 From 45ff2b685a6e8588ed637ba711b9a42ad1963066 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 14 Sep 2016 11:51:42 +0100 Subject: mfd: db8500-prcmu: Remove unused *prcmu_set_ddr_opp() calls There are no call sites for these functions. Strip them out. Signed-off-by: Lee Jones --- drivers/mfd/db8500-prcmu.c | 19 ------------------- include/linux/mfd/db8500-prcmu.h | 6 ------ include/linux/mfd/dbx500-prcmu.h | 9 --------- 3 files changed, 34 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 388e268b9bcf..ca38a6a14110 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -938,25 +938,6 @@ int db8500_prcmu_get_ddr_opp(void) return readb(PRCM_DDR_SUBSYS_APE_MINBW); } -/** - * db8500_set_ddr_opp - set the appropriate DDR OPP - * @opp: The new DDR operating point to which transition is to be made - * Returns: 0 on success, non-zero on failure - * - * This function sets the operating point of the DDR. - */ -static bool enable_set_ddr_opp; -int db8500_prcmu_set_ddr_opp(u8 opp) -{ - if (opp < DDR_100_OPP || opp > DDR_25_OPP) - return -EINVAL; - /* Changing the DDR OPP can hang the hardware pre-v21 */ - if (enable_set_ddr_opp) - writeb(opp, PRCM_DDR_SUBSYS_APE_MINBW); - - return 0; -} - /* Divide the frequency of certain clocks by 2 for APE_50_PARTLY_25_OPP. */ static void request_even_slower_clocks(bool enable) { diff --git a/include/linux/mfd/db8500-prcmu.h b/include/linux/mfd/db8500-prcmu.h index 0bd69446bb05..7ba67b55b312 100644 --- a/include/linux/mfd/db8500-prcmu.h +++ b/include/linux/mfd/db8500-prcmu.h @@ -538,7 +538,6 @@ int db8500_prcmu_get_arm_opp(void); int db8500_prcmu_set_ape_opp(u8 opp); int db8500_prcmu_get_ape_opp(void); int db8500_prcmu_request_ape_opp_100_voltage(bool enable); -int db8500_prcmu_set_ddr_opp(u8 opp); int db8500_prcmu_get_ddr_opp(void); u32 db8500_prcmu_read(unsigned int reg); @@ -594,11 +593,6 @@ static inline int prcmu_release_usb_wakeup_state(void) return 0; } -static inline int db8500_prcmu_set_ddr_opp(u8 opp) -{ - return 0; -} - static inline int db8500_prcmu_get_ddr_opp(void) { return DDR_100_OPP; diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h index 5d374601404c..2e2c6a63a065 100644 --- a/include/linux/mfd/dbx500-prcmu.h +++ b/include/linux/mfd/dbx500-prcmu.h @@ -269,10 +269,6 @@ unsigned long prcmu_clock_rate(u8 clock); long prcmu_round_clock_rate(u8 clock, unsigned long rate); int prcmu_set_clock_rate(u8 clock, unsigned long rate); -static inline int prcmu_set_ddr_opp(u8 opp) -{ - return db8500_prcmu_set_ddr_opp(opp); -} static inline int prcmu_get_ddr_opp(void) { return db8500_prcmu_get_ddr_opp(); @@ -489,11 +485,6 @@ static inline int prcmu_get_arm_opp(void) return ARM_100_OPP; } -static inline int prcmu_set_ddr_opp(u8 opp) -{ - return 0; -} - static inline int prcmu_get_ddr_opp(void) { return DDR_100_OPP; -- cgit v1.2.3 From 0133d323463ec20131f7fd5a70051bd89db4f7a1 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 31 Aug 2016 14:46:21 +0300 Subject: mfd: twl6040: Register child device for twl6040-pdmclk The McPDM in OMAP4/5 is using the pdmclk from twl6040 as functional clock. The twl6040-pdmclk driver provides a clock which can be used to make sure that the pdmclk is active when the McPDM is in use. Signed-off-by: Peter Ujfalusi Acked-by: Rob Herring Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/twl6040.txt | 1 + drivers/mfd/twl6040.c | 5 +++++ include/linux/mfd/twl6040.h | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include/linux/mfd') diff --git a/Documentation/devicetree/bindings/mfd/twl6040.txt b/Documentation/devicetree/bindings/mfd/twl6040.txt index e6afdfa3543d..9a98ee7c323d 100644 --- a/Documentation/devicetree/bindings/mfd/twl6040.txt +++ b/Documentation/devicetree/bindings/mfd/twl6040.txt @@ -12,6 +12,7 @@ Required properties: - interrupt-parent: The parent interrupt controller - gpio-controller: - #gpio-cells = <1>: twl6040 provides GPO lines. +- #clock-cells = <0>; twl6040 is a provider of pdmclk which is used by McPDM - twl6040,audpwron-gpio: Power on GPIO line for the twl6040 - vio-supply: Regulator for the twl6040 VIO supply diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index c3ad59ef43dd..d66502d36ba0 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -783,6 +783,11 @@ static int twl6040_probe(struct i2c_client *client, cell->name = "twl6040-gpo"; children++; + /* PDM clock support */ + cell = &twl6040->cells[children]; + cell->name = "twl6040-pdmclk"; + children++; + /* The chip is powered down so mark regmap to cache only and dirty */ regcache_cache_only(twl6040->regmap, true); regcache_mark_dirty(twl6040->regmap); diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index 36795a1be479..a2e88761c09f 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -168,7 +168,7 @@ #define TWL6040_VIBROCDET 0x20 #define TWL6040_TSHUTDET 0x40 -#define TWL6040_CELLS 3 +#define TWL6040_CELLS 4 #define TWL6040_REV_ES1_0 0x00 #define TWL6040_REV_ES1_1 0x01 /* Rev ES1.1 and ES1.2 */ -- cgit v1.2.3 From 1961531d1a0a011c77bfaafc8412e84a919d1747 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 20 Sep 2016 16:30:13 +0100 Subject: mfd: arizona: Remove arizona_of_get_named_gpio helper function This function is only used in a single place and no new users will be added as all the devices other required GPIOs are already handled. As such just merge the code back into the calling function. Signed-off-by: Charles Keepax --- drivers/mfd/arizona-core.c | 27 +++++++-------------------- include/linux/mfd/arizona/core.h | 3 --- 2 files changed, 7 insertions(+), 23 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 672924d5ec9d..dcee48fc2e06 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -803,25 +803,6 @@ unsigned long arizona_of_get_type(struct device *dev) } EXPORT_SYMBOL_GPL(arizona_of_get_type); -int arizona_of_get_named_gpio(struct arizona *arizona, const char *prop, - bool mandatory) -{ - int gpio; - - gpio = of_get_named_gpio(arizona->dev->of_node, prop, 0); - if (gpio < 0) { - if (mandatory) - dev_err(arizona->dev, - "Mandatory DT gpio %s missing/malformed: %d\n", - prop, gpio); - - gpio = 0; - } - - return gpio; -} -EXPORT_SYMBOL_GPL(arizona_of_get_named_gpio); - static int arizona_of_get_core_pdata(struct arizona *arizona) { struct arizona_pdata *pdata = &arizona->pdata; @@ -832,7 +813,13 @@ static int arizona_of_get_core_pdata(struct arizona *arizona) int ret, i; int count = 0; - pdata->reset = arizona_of_get_named_gpio(arizona, "wlf,reset", true); + pdata->reset = of_get_named_gpio(arizona->dev->of_node, "wlf,reset", 0); + if (pdata->reset < 0) { + dev_err(arizona->dev, "Reset GPIO missing/malformed: %d\n", + pdata->reset); + + pdata->reset = 0; + } ret = of_property_read_u32_array(arizona->dev->of_node, "wlf,gpio-defaults", diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index b9909bb0642b..b31b3be7f8c9 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -191,7 +191,4 @@ int cs47l24_patch(struct arizona *arizona); int wm8997_patch(struct arizona *arizona); int wm8998_patch(struct arizona *arizona); -extern int arizona_of_get_named_gpio(struct arizona *arizona, const char *prop, - bool mandatory); - #endif -- cgit v1.2.3