From 3e6cae8e5d40b8bdee1815124b5e36abcc746f56 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Fri, 15 May 2020 10:57:06 -0700 Subject: dt-bindings: mfd: Add Gateworks System Controller bindings This patch adds documentation of device-tree bindings for the Gateworks System Controller (GSC). Signed-off-by: Tim Harvey Reviewed-by: Rob Herring Signed-off-by: Lee Jones --- .../devicetree/bindings/mfd/gateworks-gsc.yaml | 196 +++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml diff --git a/Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml b/Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml new file mode 100644 index 000000000000..487a8445722e --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml @@ -0,0 +1,196 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/gateworks-gsc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Gateworks System Controller + +description: | + The Gateworks System Controller (GSC) is a device present across various + Gateworks product families that provides a set of system related features + such as the following (refer to the board hardware user manuals to see what + features are present) + - Watchdog Timer + - GPIO + - Pushbutton controller + - Hardware monitor with ADC's for temperature and voltage rails and + fan controller + +maintainers: + - Tim Harvey + - Robert Jones + +properties: + $nodename: + pattern: "gsc@[0-9a-f]{1,2}" + compatible: + const: gw,gsc + + reg: + description: I2C device address + maxItems: 1 + + interrupts: + maxItems: 1 + + interrupt-controller: true + + "#interrupt-cells": + const: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + adc: + type: object + description: Optional hardware monitoring module + + properties: + compatible: + const: gw,gsc-adc + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + patternProperties: + "^channel@[0-9]+$": + type: object + description: | + Properties for a single ADC which can report cooked values + (i.e. temperature sensor based on thermister), raw values + (i.e. voltage rail with a pre-scaling resistor divider). + + properties: + reg: + description: Register of the ADC + maxItems: 1 + + label: + description: Name of the ADC input + + gw,mode: + description: | + conversion mode: + 0 - temperature, in C*10 + 1 - pre-scaled voltage value + 2 - scaled voltage based on an optional resistor divider + and optional offset + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2] + + gw,voltage-divider-ohms: + description: Values of resistors for divider on raw ADC input + maxItems: 2 + items: + minimum: 1000 + maximum: 1000000 + + gw,voltage-offset-microvolt: + description: | + A positive voltage offset to apply to a raw ADC + (i.e. to compensate for a diode drop). + minimum: 0 + maximum: 1000000 + + required: + - gw,mode + - reg + - label + + required: + - compatible + - "#address-cells" + - "#size-cells" + +patternProperties: + "^fan-controller@[0-9a-f]+$": + type: object + description: Optional fan controller + + properties: + compatible: + const: gw,gsc-fan + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + reg: + description: The fan controller base address + maxItems: 1 + + required: + - compatible + - reg + - "#address-cells" + - "#size-cells" + +required: + - compatible + - reg + - interrupts + - interrupt-controller + - "#interrupt-cells" + - "#address-cells" + - "#size-cells" + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + gsc@20 { + compatible = "gw,gsc"; + reg = <0x20>; + interrupt-parent = <&gpio1>; + interrupts = <4 GPIO_ACTIVE_LOW>; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + + adc { + compatible = "gw,gsc-adc"; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { /* A0: Board Temperature */ + reg = <0x00>; + label = "temp"; + gw,mode = <0>; + }; + + channel@2 { /* A1: Input Voltage (raw ADC) */ + reg = <0x02>; + label = "vdd_vin"; + gw,mode = <1>; + gw,voltage-divider-ohms = <22100 1000>; + gw,voltage-offset-microvolt = <800000>; + }; + + channel@b { /* A2: Battery voltage */ + reg = <0x0b>; + label = "vdd_bat"; + gw,mode = <1>; + }; + }; + + fan-controller@2c { + #address-cells = <1>; + #size-cells = <0>; + compatible = "gw,gsc-fan"; + reg = <0x2c>; + }; + }; + }; -- cgit v1.2.3 From d85234994b2fb2d88fadd3d9e60385b02b244dac Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Fri, 15 May 2020 10:57:07 -0700 Subject: mfd: Add Gateworks System Controller core driver The Gateworks System Controller (GSC) is an I2C slave controller implemented with an MSP430 micro-controller whose firmware embeds the following features: - I/O expander (16 GPIO's) using PCA955x protocol - Real Time Clock using DS1672 protocol - User EEPROM using AT24 protocol - HWMON using custom protocol - Interrupt controller with tamper detect, user pushbotton - Watchdog controller capable of full board power-cycle - Power Control capable of full board power-cycle see http://trac.gateworks.com/wiki/gsc for more details Signed-off-by: Tim Harvey Signed-off-by: Lee Jones --- MAINTAINERS | 8 ++ drivers/mfd/Kconfig | 15 +++ drivers/mfd/Makefile | 1 + drivers/mfd/gateworks-gsc.c | 277 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/gsc.h | 76 ++++++++++++ 5 files changed, 377 insertions(+) create mode 100644 drivers/mfd/gateworks-gsc.c create mode 100644 include/linux/mfd/gsc.h diff --git a/MAINTAINERS b/MAINTAINERS index e64e5db31497..a5d5e5d963f1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7028,6 +7028,14 @@ F: kernel/futex.c F: tools/perf/bench/futex* F: tools/testing/selftests/futex/ +GATEWORKS SYSTEM CONTROLLER (GSC) DRIVER +M: Tim Harvey +M: Robert Jones +S: Maintained +F: Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml +F: drivers/mfd/gateworks-gsc.c +F: include/linux/mfd/gsc.h + GASKET DRIVER FRAMEWORK M: Rob Springer M: Todd Poynor diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 0a59249198d3..05448e78e5b8 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -407,6 +407,21 @@ config MFD_EXYNOS_LPASS Select this option to enable support for Samsung Exynos Low Power Audio Subsystem. +config MFD_GATEWORKS_GSC + tristate "Gateworks System Controller" + depends on (I2C && OF) + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + help + Enable support for the Gateworks System Controller (GSC) found + on Gateworks Single Board Computers supporting system functions + such as push-button monitor, multiple ADC's for voltage and + temperature monitoring, fan controller and watchdog monitor. + This driver provides common support for accessing the device. + Additional drivers must be enabled in order to use the + functionality of the device. + config MFD_MC13XXX tristate depends on (SPI_MASTER || I2C) diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f935d10cbf0f..ed433aed7010 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o obj-$(CONFIG_MFD_CROS_EC_DEV) += cros_ec_dev.o obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o +obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o diff --git a/drivers/mfd/gateworks-gsc.c b/drivers/mfd/gateworks-gsc.c new file mode 100644 index 000000000000..576da62fbb0c --- /dev/null +++ b/drivers/mfd/gateworks-gsc.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The Gateworks System Controller (GSC) is a multi-function + * device designed for use in Gateworks Single Board Computers. + * The control interface is I2C, with an interrupt. The device supports + * system functions such as push-button monitoring, multiple ADC's for + * voltage and temperature monitoring, fan controller and watchdog monitor. + * + * Copyright (C) 2020 Gateworks Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * The GSC suffers from an errata where occasionally during + * ADC cycles the chip can NAK I2C transactions. To ensure we have reliable + * register access we place retries around register access. + */ +#define I2C_RETRIES 3 + +int gsc_write(void *context, unsigned int reg, unsigned int val) +{ + struct i2c_client *client = context; + int retry, ret; + + for (retry = 0; retry < I2C_RETRIES; retry++) { + ret = i2c_smbus_write_byte_data(client, reg, val); + /* + * -EAGAIN returned when the i2c host controller is busy + * -EIO returned when i2c device is busy + */ + if (ret != -EAGAIN && ret != -EIO) + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(gsc_write); + +int gsc_read(void *context, unsigned int reg, unsigned int *val) +{ + struct i2c_client *client = context; + int retry, ret; + + for (retry = 0; retry < I2C_RETRIES; retry++) { + ret = i2c_smbus_read_byte_data(client, reg); + /* + * -EAGAIN returned when the i2c host controller is busy + * -EIO returned when i2c device is busy + */ + if (ret != -EAGAIN && ret != -EIO) + break; + } + *val = ret & 0xff; + + return 0; +} +EXPORT_SYMBOL_GPL(gsc_read); + +/* + * gsc_powerdown - API to use GSC to power down board for a specific time + * + * secs - number of seconds to remain powered off + */ +static int gsc_powerdown(struct gsc_dev *gsc, unsigned long secs) +{ + int ret; + unsigned char regs[4]; + + dev_info(&gsc->i2c->dev, "GSC powerdown for %ld seconds\n", + secs); + + put_unaligned_le32(secs, regs); + ret = regmap_bulk_write(gsc->regmap, GSC_TIME_ADD, regs, 4); + if (ret) + return ret; + + ret = regmap_update_bits(gsc->regmap, GSC_CTRL_1, + BIT(GSC_CTRL_1_SLEEP_ADD), + BIT(GSC_CTRL_1_SLEEP_ADD)); + if (ret) + return ret; + + ret = regmap_update_bits(gsc->regmap, GSC_CTRL_1, + BIT(GSC_CTRL_1_SLEEP_ACTIVATE) | + BIT(GSC_CTRL_1_SLEEP_ENABLE), + BIT(GSC_CTRL_1_SLEEP_ACTIVATE) | + BIT(GSC_CTRL_1_SLEEP_ENABLE)); + + + return ret; +} + +static ssize_t gsc_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct gsc_dev *gsc = dev_get_drvdata(dev); + const char *name = attr->attr.name; + int rz = 0; + + if (strcasecmp(name, "fw_version") == 0) + rz = sprintf(buf, "%d\n", gsc->fwver); + else if (strcasecmp(name, "fw_crc") == 0) + rz = sprintf(buf, "0x%04x\n", gsc->fwcrc); + else + dev_err(dev, "invalid command: '%s'\n", name); + + return rz; +} + +static ssize_t gsc_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gsc_dev *gsc = dev_get_drvdata(dev); + const char *name = attr->attr.name; + long value; + + if (strcasecmp(name, "powerdown") == 0) { + if (kstrtol(buf, 0, &value) == 0) + gsc_powerdown(gsc, value); + } else { + dev_err(dev, "invalid command: '%s\n", name); + } + + return count; +} + +static struct device_attribute attr_fwver = + __ATTR(fw_version, 0440, gsc_show, NULL); +static struct device_attribute attr_fwcrc = + __ATTR(fw_crc, 0440, gsc_show, NULL); +static struct device_attribute attr_pwrdown = + __ATTR(powerdown, 0220, NULL, gsc_store); + +static struct attribute *gsc_attrs[] = { + &attr_fwver.attr, + &attr_fwcrc.attr, + &attr_pwrdown.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = gsc_attrs, +}; + +static const struct of_device_id gsc_of_match[] = { + { .compatible = "gw,gsc", }, + { } +}; +MODULE_DEVICE_TABLE(of, gsc_of_match); + +static struct regmap_bus gsc_regmap_bus = { + .reg_read = gsc_read, + .reg_write = gsc_write, +}; + +static const struct regmap_config gsc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, + .max_register = GSC_WP, +}; + +static const struct regmap_irq gsc_irqs[] = { + REGMAP_IRQ_REG(GSC_IRQ_PB, 0, BIT(GSC_IRQ_PB)), + REGMAP_IRQ_REG(GSC_IRQ_KEY_ERASED, 0, BIT(GSC_IRQ_KEY_ERASED)), + REGMAP_IRQ_REG(GSC_IRQ_EEPROM_WP, 0, BIT(GSC_IRQ_EEPROM_WP)), + REGMAP_IRQ_REG(GSC_IRQ_RESV, 0, BIT(GSC_IRQ_RESV)), + REGMAP_IRQ_REG(GSC_IRQ_GPIO, 0, BIT(GSC_IRQ_GPIO)), + REGMAP_IRQ_REG(GSC_IRQ_TAMPER, 0, BIT(GSC_IRQ_TAMPER)), + REGMAP_IRQ_REG(GSC_IRQ_WDT_TIMEOUT, 0, BIT(GSC_IRQ_WDT_TIMEOUT)), + REGMAP_IRQ_REG(GSC_IRQ_SWITCH_HOLD, 0, BIT(GSC_IRQ_SWITCH_HOLD)), +}; + +static const struct regmap_irq_chip gsc_irq_chip = { + .name = "gateworks-gsc", + .irqs = gsc_irqs, + .num_irqs = ARRAY_SIZE(gsc_irqs), + .num_regs = 1, + .status_base = GSC_IRQ_STATUS, + .mask_base = GSC_IRQ_ENABLE, + .mask_invert = true, + .ack_base = GSC_IRQ_STATUS, + .ack_invert = true, +}; + +static int gsc_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct gsc_dev *gsc; + struct regmap_irq_chip_data *irq_data; + int ret; + unsigned int reg; + + gsc = devm_kzalloc(dev, sizeof(*gsc), GFP_KERNEL); + if (!gsc) + return -ENOMEM; + + gsc->dev = &client->dev; + gsc->i2c = client; + i2c_set_clientdata(client, gsc); + + gsc->regmap = devm_regmap_init(dev, &gsc_regmap_bus, client, + &gsc_regmap_config); + if (IS_ERR(gsc->regmap)) + return PTR_ERR(gsc->regmap); + + if (regmap_read(gsc->regmap, GSC_FW_VER, ®)) + return -EIO; + gsc->fwver = reg; + + regmap_read(gsc->regmap, GSC_FW_CRC, ®); + gsc->fwcrc = reg; + regmap_read(gsc->regmap, GSC_FW_CRC + 1, ®); + gsc->fwcrc |= reg << 8; + + gsc->i2c_hwmon = devm_i2c_new_dummy_device(dev, client->adapter, + GSC_HWMON); + if (IS_ERR(gsc->i2c_hwmon)) { + dev_err(dev, "Failed to allocate I2C device for HWMON\n"); + return PTR_ERR(gsc->i2c_hwmon); + } + + ret = devm_regmap_add_irq_chip(dev, gsc->regmap, client->irq, + IRQF_ONESHOT | IRQF_SHARED | + IRQF_TRIGGER_FALLING, 0, + &gsc_irq_chip, &irq_data); + if (ret) + return ret; + + dev_info(dev, "Gateworks System Controller v%d: fw 0x%04x\n", + gsc->fwver, gsc->fwcrc); + + ret = sysfs_create_group(&dev->kobj, &attr_group); + if (ret) + dev_err(dev, "failed to create sysfs attrs\n"); + + ret = devm_of_platform_populate(dev); + if (ret) { + sysfs_remove_group(&dev->kobj, &attr_group); + return ret; + } + + return 0; +} + +static int gsc_remove(struct i2c_client *client) +{ + sysfs_remove_group(&client->dev.kobj, &attr_group); + + return 0; +} + +static struct i2c_driver gsc_driver = { + .driver = { + .name = "gateworks-gsc", + .of_match_table = gsc_of_match, + }, + .probe_new = gsc_probe, + .remove = gsc_remove, +}; +module_i2c_driver(gsc_driver); + +MODULE_AUTHOR("Tim Harvey "); +MODULE_DESCRIPTION("I2C Core interface for GSC"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/gsc.h b/include/linux/mfd/gsc.h new file mode 100644 index 000000000000..6bd639c285b4 --- /dev/null +++ b/include/linux/mfd/gsc.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2020 Gateworks Corporation + */ +#ifndef __LINUX_MFD_GSC_H_ +#define __LINUX_MFD_GSC_H_ + +#include + +/* Device Addresses */ +#define GSC_MISC 0x20 +#define GSC_UPDATE 0x21 +#define GSC_GPIO 0x23 +#define GSC_HWMON 0x29 +#define GSC_EEPROM0 0x50 +#define GSC_EEPROM1 0x51 +#define GSC_EEPROM2 0x52 +#define GSC_EEPROM3 0x53 +#define GSC_RTC 0x68 + +/* Register offsets */ +enum { + GSC_CTRL_0 = 0x00, + GSC_CTRL_1 = 0x01, + GSC_TIME = 0x02, + GSC_TIME_ADD = 0x06, + GSC_IRQ_STATUS = 0x0A, + GSC_IRQ_ENABLE = 0x0B, + GSC_FW_CRC = 0x0C, + GSC_FW_VER = 0x0E, + GSC_WP = 0x0F, +}; + +/* Bit definitions */ +#define GSC_CTRL_0_PB_HARD_RESET 0 +#define GSC_CTRL_0_PB_CLEAR_SECURE_KEY 1 +#define GSC_CTRL_0_PB_SOFT_POWER_DOWN 2 +#define GSC_CTRL_0_PB_BOOT_ALTERNATE 3 +#define GSC_CTRL_0_PERFORM_CRC 4 +#define GSC_CTRL_0_TAMPER_DETECT 5 +#define GSC_CTRL_0_SWITCH_HOLD 6 + +#define GSC_CTRL_1_SLEEP_ENABLE 0 +#define GSC_CTRL_1_SLEEP_ACTIVATE 1 +#define GSC_CTRL_1_SLEEP_ADD 2 +#define GSC_CTRL_1_SLEEP_NOWAKEPB 3 +#define GSC_CTRL_1_WDT_TIME 4 +#define GSC_CTRL_1_WDT_ENABLE 5 +#define GSC_CTRL_1_SWITCH_BOOT_ENABLE 6 +#define GSC_CTRL_1_SWITCH_BOOT_CLEAR 7 + +#define GSC_IRQ_PB 0 +#define GSC_IRQ_KEY_ERASED 1 +#define GSC_IRQ_EEPROM_WP 2 +#define GSC_IRQ_RESV 3 +#define GSC_IRQ_GPIO 4 +#define GSC_IRQ_TAMPER 5 +#define GSC_IRQ_WDT_TIMEOUT 6 +#define GSC_IRQ_SWITCH_HOLD 7 + +int gsc_read(void *context, unsigned int reg, unsigned int *val); +int gsc_write(void *context, unsigned int reg, unsigned int val); + +struct gsc_dev { + struct device *dev; + + struct i2c_client *i2c; /* 0x20: interrupt controller, WDT */ + struct i2c_client *i2c_hwmon; /* 0x29: hwmon, fan controller */ + + struct regmap *regmap; + + unsigned int fwver; + unsigned short fwcrc; +}; + +#endif /* __LINUX_MFD_GSC_H_ */ -- cgit v1.2.3 From 3bce5377ef66a8700dcf7a9cb89b7aeb99326cb7 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Fri, 15 May 2020 10:57:08 -0700 Subject: hwmon: Add Gateworks System Controller support The Gateworks System Controller has a hwmon sub-component that exposes up to 16 ADC's, some of which are temperature sensors, others which are voltage inputs. The ADC configuration (register mapping and name) is configured via device-tree and varies board to board. Signed-off-by: Tim Harvey Reviewed-by: Guenter Roeck Signed-off-by: Lee Jones --- Documentation/hwmon/gsc-hwmon.rst | 53 +++++ Documentation/hwmon/index.rst | 1 + MAINTAINERS | 3 + drivers/hwmon/Kconfig | 9 + drivers/hwmon/Makefile | 1 + drivers/hwmon/gsc-hwmon.c | 390 ++++++++++++++++++++++++++++++++ include/linux/platform_data/gsc_hwmon.h | 44 ++++ 7 files changed, 501 insertions(+) create mode 100644 Documentation/hwmon/gsc-hwmon.rst create mode 100644 drivers/hwmon/gsc-hwmon.c create mode 100644 include/linux/platform_data/gsc_hwmon.h diff --git a/Documentation/hwmon/gsc-hwmon.rst b/Documentation/hwmon/gsc-hwmon.rst new file mode 100644 index 000000000000..ffac392a7129 --- /dev/null +++ b/Documentation/hwmon/gsc-hwmon.rst @@ -0,0 +1,53 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver gsc-hwmon +======================= + +Supported chips: Gateworks GSC +Datasheet: http://trac.gateworks.com/wiki/gsc +Author: Tim Harvey + +Description: +------------ + +This driver supports hardware monitoring for the temperature sensor, +various ADC's connected to the GSC, and optional FAN controller available +on some boards. + + +Voltage Monitoring +------------------ + +The voltage inputs are scaled either internally or by the driver depending +on the GSC version and firmware. The values returned by the driver do not need +further scaling. The voltage input labels provide the voltage rail name: + +inX_input Measured voltage (mV). +inX_label Name of voltage rail. + + +Temperature Monitoring +---------------------- + +Temperatures are measured with 12-bit or 10-bit resolution and are scaled +either internally or by the driver depending on the GSC version and firmware. +The values returned by the driver reflect millidegree Celcius: + +tempX_input Measured temperature. +tempX_label Name of temperature input. + + +PWM Output Control +------------------ + +The GSC features 1 PWM output that operates in automatic mode where the +PWM value will be scalled depending on 6 temperature boundaries. +The tempeature boundaries are read-write and in millidegree Celcius and the +read-only PWM values range from 0 (off) to 255 (full speed). +Fan speed will be set to minimum (off) when the temperature sensor reads +less than pwm1_auto_point1_temp and maximum when the temperature sensor +equals or exceeds pwm1_auto_point6_temp. + +pwm1_auto_point[1-6]_pwm PWM value. +pwm1_auto_point[1-6]_temp Temperature boundary. + diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 8ef62fd39787..1c02aa697a02 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -60,6 +60,7 @@ Hardware Monitoring Kernel Drivers ftsteutates g760a g762 + gsc-hwmon gl518sm hih6130 ibmaem diff --git a/MAINTAINERS b/MAINTAINERS index a5d5e5d963f1..d9cee5698bcc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7035,6 +7035,9 @@ S: Maintained F: Documentation/devicetree/bindings/mfd/gateworks-gsc.yaml F: drivers/mfd/gateworks-gsc.c F: include/linux/mfd/gsc.h +F: Documentation/hwmon/gsc-hwmon.rst +F: drivers/hwmon/gsc-hwmon.c +F: include/linux/platform_data/gsc_hwmon.h GASKET DRIVER FRAMEWORK M: Rob Springer diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 05a30832c6ba..d008b445baba 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -523,6 +523,15 @@ config SENSORS_F75375S This driver can also be built as a module. If so, the module will be called f75375s. +config SENSORS_GSC + tristate "Gateworks System Controller ADC" + depends on MFD_GATEWORKS_GSC + help + Support for the Gateworks System Controller A/D converters. + + To compile this driver as a module, choose M here: + the module will be called gsc-hwmon. + config SENSORS_MC13783_ADC tristate "Freescale MC13783/MC13892 ADC" depends on MFD_MC13XXX diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index b0b9c8e57176..a6564d00d94c 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_SENSORS_G760A) += g760a.o obj-$(CONFIG_SENSORS_G762) += g762.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o +obj-$(CONFIG_SENSORS_GSC) += gsc-hwmon.o obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o diff --git a/drivers/hwmon/gsc-hwmon.c b/drivers/hwmon/gsc-hwmon.c new file mode 100644 index 000000000000..2137bc65829d --- /dev/null +++ b/drivers/hwmon/gsc-hwmon.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Gateworks System Controller Hardware Monitor module + * + * Copyright (C) 2020 Gateworks Corporation + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define GSC_HWMON_MAX_TEMP_CH 16 +#define GSC_HWMON_MAX_IN_CH 16 + +#define GSC_HWMON_RESOLUTION 12 +#define GSC_HWMON_VREF 2500 + +struct gsc_hwmon_data { + struct gsc_dev *gsc; + struct gsc_hwmon_platform_data *pdata; + struct regmap *regmap; + const struct gsc_hwmon_channel *temp_ch[GSC_HWMON_MAX_TEMP_CH]; + const struct gsc_hwmon_channel *in_ch[GSC_HWMON_MAX_IN_CH]; + u32 temp_config[GSC_HWMON_MAX_TEMP_CH + 1]; + u32 in_config[GSC_HWMON_MAX_IN_CH + 1]; + struct hwmon_channel_info temp_info; + struct hwmon_channel_info in_info; + const struct hwmon_channel_info *info[3]; + struct hwmon_chip_info chip; +}; + +static struct regmap_bus gsc_hwmon_regmap_bus = { + .reg_read = gsc_read, + .reg_write = gsc_write, +}; + +static const struct regmap_config gsc_hwmon_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, +}; + +static ssize_t pwm_auto_point_temp_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + u8 reg = hwmon->pdata->fan_base + (2 * attr->index); + u8 regs[2]; + int ret; + + ret = regmap_bulk_read(hwmon->regmap, reg, regs, 2); + if (ret) + return ret; + + ret = regs[0] | regs[1] << 8; + return sprintf(buf, "%d\n", ret * 10); +} + +static ssize_t pwm_auto_point_temp_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + u8 reg = hwmon->pdata->fan_base + (2 * attr->index); + u8 regs[2]; + long temp; + int err; + + if (kstrtol(buf, 10, &temp)) + return -EINVAL; + + temp = clamp_val(temp, 0, 10000); + temp = DIV_ROUND_CLOSEST(temp, 10); + + regs[0] = temp & 0xff; + regs[1] = (temp >> 8) & 0xff; + err = regmap_bulk_write(hwmon->regmap, reg, regs, 2); + if (err) + return err; + + return count; +} + +static ssize_t pwm_auto_point_pwm_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + return sprintf(buf, "%d\n", 255 * (50 + (attr->index * 10)) / 100); +} + +static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point1_pwm, pwm_auto_point_pwm, 0); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_temp, pwm_auto_point_temp, 0); + +static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point2_pwm, pwm_auto_point_pwm, 1); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_temp, pwm_auto_point_temp, 1); + +static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point3_pwm, pwm_auto_point_pwm, 2); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point3_temp, pwm_auto_point_temp, 2); + +static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point4_pwm, pwm_auto_point_pwm, 3); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point4_temp, pwm_auto_point_temp, 3); + +static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point5_pwm, pwm_auto_point_pwm, 4); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point5_temp, pwm_auto_point_temp, 4); + +static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point6_pwm, pwm_auto_point_pwm, 5); +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point6_temp, pwm_auto_point_temp, 5); + +static struct attribute *gsc_hwmon_attributes[] = { + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point6_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point6_temp.dev_attr.attr, + NULL +}; + +static const struct attribute_group gsc_hwmon_group = { + .attrs = gsc_hwmon_attributes, +}; +__ATTRIBUTE_GROUPS(gsc_hwmon); + +static int +gsc_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +{ + struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev); + const struct gsc_hwmon_channel *ch; + int sz, ret; + long tmp; + u8 buf[3]; + + switch (type) { + case hwmon_in: + ch = hwmon->in_ch[channel]; + break; + case hwmon_temp: + ch = hwmon->temp_ch[channel]; + break; + default: + return -EOPNOTSUPP; + } + + sz = (ch->mode == mode_voltage) ? 3 : 2; + ret = regmap_bulk_read(hwmon->regmap, ch->reg, buf, sz); + if (ret) + return ret; + + tmp = 0; + while (sz-- > 0) + tmp |= (buf[sz] << (8 * sz)); + + switch (ch->mode) { + case mode_temperature: + if (tmp > 0x8000) + tmp -= 0xffff; + break; + case mode_voltage_raw: + tmp = clamp_val(tmp, 0, BIT(GSC_HWMON_RESOLUTION)); + /* scale based on ref voltage and ADC resolution */ + tmp *= GSC_HWMON_VREF; + tmp >>= GSC_HWMON_RESOLUTION; + /* scale based on optional voltage divider */ + if (ch->vdiv[0] && ch->vdiv[1]) { + tmp *= (ch->vdiv[0] + ch->vdiv[1]); + tmp /= ch->vdiv[1]; + } + /* adjust by uV offset */ + tmp += ch->mvoffset; + break; + case mode_voltage: + /* no adjustment needed */ + break; + } + + *val = tmp; + + return 0; +} + +static int +gsc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **buf) +{ + struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev); + + switch (type) { + case hwmon_in: + *buf = hwmon->in_ch[channel]->name; + break; + case hwmon_temp: + *buf = hwmon->temp_ch[channel]->name; + break; + default: + return -ENOTSUPP; + } + + return 0; +} + +static umode_t +gsc_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr, + int ch) +{ + return 0444; +} + +static const struct hwmon_ops gsc_hwmon_ops = { + .is_visible = gsc_hwmon_is_visible, + .read = gsc_hwmon_read, + .read_string = gsc_hwmon_read_string, +}; + +static struct gsc_hwmon_platform_data * +gsc_hwmon_get_devtree_pdata(struct device *dev) +{ + struct gsc_hwmon_platform_data *pdata; + struct gsc_hwmon_channel *ch; + struct fwnode_handle *child; + struct device_node *fan; + int nchannels; + + nchannels = device_get_child_node_count(dev); + if (nchannels == 0) + return ERR_PTR(-ENODEV); + + pdata = devm_kzalloc(dev, + sizeof(*pdata) + nchannels * sizeof(*ch), + GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + ch = (struct gsc_hwmon_channel *)(pdata + 1); + pdata->channels = ch; + pdata->nchannels = nchannels; + + /* fan controller base address */ + fan = of_find_compatible_node(dev->parent->of_node, NULL, "gw,gsc-fan"); + if (fan && of_property_read_u32(fan, "reg", &pdata->fan_base)) { + dev_err(dev, "fan node without base\n"); + return ERR_PTR(-EINVAL); + } + + /* allocate structures for channels and count instances of each type */ + device_for_each_child_node(dev, child) { + if (fwnode_property_read_string(child, "label", &ch->name)) { + dev_err(dev, "channel without label\n"); + fwnode_handle_put(child); + return ERR_PTR(-EINVAL); + } + if (fwnode_property_read_u32(child, "reg", &ch->reg)) { + dev_err(dev, "channel without reg\n"); + fwnode_handle_put(child); + return ERR_PTR(-EINVAL); + } + if (fwnode_property_read_u32(child, "gw,mode", &ch->mode)) { + dev_err(dev, "channel without mode\n"); + fwnode_handle_put(child); + return ERR_PTR(-EINVAL); + } + if (ch->mode > mode_max) { + dev_err(dev, "invalid channel mode\n"); + fwnode_handle_put(child); + return ERR_PTR(-EINVAL); + } + + if (!fwnode_property_read_u32(child, + "gw,voltage-offset-microvolt", + &ch->mvoffset)) + ch->mvoffset /= 1000; + fwnode_property_read_u32_array(child, + "gw,voltage-divider-ohms", + ch->vdiv, ARRAY_SIZE(ch->vdiv)); + ch++; + } + + return pdata; +} + +static int gsc_hwmon_probe(struct platform_device *pdev) +{ + struct gsc_dev *gsc = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct device *hwmon_dev; + struct gsc_hwmon_platform_data *pdata = dev_get_platdata(dev); + struct gsc_hwmon_data *hwmon; + const struct attribute_group **groups; + int i, i_in, i_temp; + + if (!pdata) { + pdata = gsc_hwmon_get_devtree_pdata(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + + hwmon = devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL); + if (!hwmon) + return -ENOMEM; + hwmon->gsc = gsc; + hwmon->pdata = pdata; + + hwmon->regmap = devm_regmap_init(dev, &gsc_hwmon_regmap_bus, + gsc->i2c_hwmon, + &gsc_hwmon_regmap_config); + if (IS_ERR(hwmon->regmap)) + return PTR_ERR(hwmon->regmap); + + for (i = 0, i_in = 0, i_temp = 0; i < hwmon->pdata->nchannels; i++) { + const struct gsc_hwmon_channel *ch = &pdata->channels[i]; + + switch (ch->mode) { + case mode_temperature: + if (i_temp == GSC_HWMON_MAX_TEMP_CH) { + dev_err(gsc->dev, "too many temp channels\n"); + return -EINVAL; + } + hwmon->temp_ch[i_temp] = ch; + hwmon->temp_config[i_temp] = HWMON_T_INPUT | + HWMON_T_LABEL; + i_temp++; + break; + case mode_voltage: + case mode_voltage_raw: + if (i_in == GSC_HWMON_MAX_IN_CH) { + dev_err(gsc->dev, "too many input channels\n"); + return -EINVAL; + } + hwmon->in_ch[i_in] = ch; + hwmon->in_config[i_in] = + HWMON_I_INPUT | HWMON_I_LABEL; + i_in++; + break; + default: + dev_err(gsc->dev, "invalid mode: %d\n", ch->mode); + return -EINVAL; + } + } + + /* setup config structures */ + hwmon->chip.ops = &gsc_hwmon_ops; + hwmon->chip.info = hwmon->info; + hwmon->info[0] = &hwmon->temp_info; + hwmon->info[1] = &hwmon->in_info; + hwmon->temp_info.type = hwmon_temp; + hwmon->temp_info.config = hwmon->temp_config; + hwmon->in_info.type = hwmon_in; + hwmon->in_info.config = hwmon->in_config; + + groups = pdata->fan_base ? gsc_hwmon_groups : NULL; + hwmon_dev = devm_hwmon_device_register_with_info(dev, + KBUILD_MODNAME, hwmon, + &hwmon->chip, groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct of_device_id gsc_hwmon_of_match[] = { + { .compatible = "gw,gsc-adc", }, + {} +}; + +static struct platform_driver gsc_hwmon_driver = { + .driver = { + .name = "gsc-hwmon", + .of_match_table = gsc_hwmon_of_match, + }, + .probe = gsc_hwmon_probe, +}; + +module_platform_driver(gsc_hwmon_driver); + +MODULE_AUTHOR("Tim Harvey "); +MODULE_DESCRIPTION("GSC hardware monitor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/gsc_hwmon.h b/include/linux/platform_data/gsc_hwmon.h new file mode 100644 index 000000000000..ec1611aff863 --- /dev/null +++ b/include/linux/platform_data/gsc_hwmon.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _GSC_HWMON_H +#define _GSC_HWMON_H + +enum gsc_hwmon_mode { + mode_temperature, + mode_voltage, + mode_voltage_raw, + mode_max, +}; + +/** + * struct gsc_hwmon_channel - configuration parameters + * @reg: I2C register offset + * @mode: channel mode + * @name: channel name + * @mvoffset: voltage offset + * @vdiv: voltage divider array (2 resistor values in milli-ohms) + */ +struct gsc_hwmon_channel { + unsigned int reg; + unsigned int mode; + const char *name; + unsigned int mvoffset; + unsigned int vdiv[2]; +}; + +/** + * struct gsc_hwmon_platform_data - platform data for gsc_hwmon driver + * @channels: pointer to array of gsc_hwmon_channel structures + * describing channels + * @nchannels: number of elements in @channels array + * @vreference: voltage reference (mV) + * @resolution: ADC bit resolution + * @fan_base: register base for FAN controller + */ +struct gsc_hwmon_platform_data { + const struct gsc_hwmon_channel *channels; + int nchannels; + unsigned int resolution; + unsigned int vreference; + unsigned int fan_base; +}; +#endif -- cgit v1.2.3 From 4e2e7cfec13afa41fcbed1d9b93d83432aa0154f Mon Sep 17 00:00:00 2001 From: Hsin-Hsiung Wang Date: Tue, 21 Apr 2020 11:00:07 +0800 Subject: mfd: mt6397: Modify suspend/resume behavior Some pmics don't need backup interrupt settings, so we change to use pm notifier for the pmics which are necessary to store settings. Signed-off-by: Hsin-Hsiung Wang Signed-off-by: Lee Jones --- drivers/mfd/mt6397-core.c | 30 ------------------------------ drivers/mfd/mt6397-irq.c | 35 ++++++++++++++++++++++++++++++++++- include/linux/mfd/mt6397/core.h | 2 ++ 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c index 0437c858d115..d2e70d834c40 100644 --- a/drivers/mfd/mt6397-core.c +++ b/drivers/mfd/mt6397-core.c @@ -100,35 +100,6 @@ static const struct mfd_cell mt6397_devs[] = { } }; -#ifdef CONFIG_PM_SLEEP -static int mt6397_irq_suspend(struct device *dev) -{ - struct mt6397_chip *chip = dev_get_drvdata(dev); - - regmap_write(chip->regmap, chip->int_con[0], chip->wake_mask[0]); - regmap_write(chip->regmap, chip->int_con[1], chip->wake_mask[1]); - - enable_irq_wake(chip->irq); - - return 0; -} - -static int mt6397_irq_resume(struct device *dev) -{ - struct mt6397_chip *chip = dev_get_drvdata(dev); - - regmap_write(chip->regmap, chip->int_con[0], chip->irq_masks_cur[0]); - regmap_write(chip->regmap, chip->int_con[1], chip->irq_masks_cur[1]); - - disable_irq_wake(chip->irq); - - return 0; -} -#endif - -static SIMPLE_DEV_PM_OPS(mt6397_pm_ops, mt6397_irq_suspend, - mt6397_irq_resume); - struct chip_data { u32 cid_addr; u32 cid_shift; @@ -238,7 +209,6 @@ static struct platform_driver mt6397_driver = { .driver = { .name = "mt6397", .of_match_table = of_match_ptr(mt6397_of_match), - .pm = &mt6397_pm_ops, }, .id_table = mt6397_id, }; diff --git a/drivers/mfd/mt6397-irq.c b/drivers/mfd/mt6397-irq.c index b2d3ce1f3115..2924919da991 100644 --- a/drivers/mfd/mt6397-irq.c +++ b/drivers/mfd/mt6397-irq.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -81,7 +82,7 @@ static struct irq_chip mt6397_irq_chip = { static void mt6397_irq_handle_reg(struct mt6397_chip *mt6397, int reg, int irqbase) { - unsigned int status; + unsigned int status = 0; int i, irq, ret; ret = regmap_read(mt6397->regmap, reg, &status); @@ -128,6 +129,36 @@ static const struct irq_domain_ops mt6397_irq_domain_ops = { .map = mt6397_irq_domain_map, }; +static int mt6397_irq_pm_notifier(struct notifier_block *notifier, + unsigned long pm_event, void *unused) +{ + struct mt6397_chip *chip = + container_of(notifier, struct mt6397_chip, pm_nb); + + switch (pm_event) { + case PM_SUSPEND_PREPARE: + regmap_write(chip->regmap, + chip->int_con[0], chip->wake_mask[0]); + regmap_write(chip->regmap, + chip->int_con[1], chip->wake_mask[1]); + enable_irq_wake(chip->irq); + break; + + case PM_POST_SUSPEND: + regmap_write(chip->regmap, + chip->int_con[0], chip->irq_masks_cur[0]); + regmap_write(chip->regmap, + chip->int_con[1], chip->irq_masks_cur[1]); + disable_irq_wake(chip->irq); + break; + + default: + break; + } + + return NOTIFY_DONE; +} + int mt6397_irq_init(struct mt6397_chip *chip) { int ret; @@ -159,6 +190,7 @@ int mt6397_irq_init(struct mt6397_chip *chip) regmap_write(chip->regmap, chip->int_con[0], 0x0); regmap_write(chip->regmap, chip->int_con[1], 0x0); + chip->pm_nb.notifier_call = mt6397_irq_pm_notifier; chip->irq_domain = irq_domain_add_linear(chip->dev->of_node, MT6397_IRQ_NR, &mt6397_irq_domain_ops, @@ -177,5 +209,6 @@ int mt6397_irq_init(struct mt6397_chip *chip) return ret; } + register_pm_notifier(&chip->pm_nb); return 0; } diff --git a/include/linux/mfd/mt6397/core.h b/include/linux/mfd/mt6397/core.h index fc88d315bdde..b81d33325ade 100644 --- a/include/linux/mfd/mt6397/core.h +++ b/include/linux/mfd/mt6397/core.h @@ -8,6 +8,7 @@ #define __MFD_MT6397_CORE_H__ #include +#include enum chip_id { MT6323_CHIP_ID = 0x23, @@ -54,6 +55,7 @@ enum mt6397_irq_numbers { struct mt6397_chip { struct device *dev; struct regmap *regmap; + struct notifier_block pm_nb; int irq; struct irq_domain *irq_domain; struct mutex irqlock; -- cgit v1.2.3 From d0c7347dc78f059fa2579a32f444b378589ad7f0 Mon Sep 17 00:00:00 2001 From: Hsin-Hsiung Wang Date: Tue, 21 Apr 2020 11:00:08 +0800 Subject: mfd: mt6397: Trim probe function to support different chips more cleanly Add new struct members for mfd-cells and irq initial function, so we can call devm_mfd_add_devices() only once. Signed-off-by: Hsin-Hsiung Wang Signed-off-by: Lee Jones --- drivers/mfd/mt6397-core.c | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c index d2e70d834c40..a313a720bb8b 100644 --- a/drivers/mfd/mt6397-core.c +++ b/drivers/mfd/mt6397-core.c @@ -103,22 +103,31 @@ static const struct mfd_cell mt6397_devs[] = { struct chip_data { u32 cid_addr; u32 cid_shift; + const struct mfd_cell *cells; + int cell_size; + int (*irq_init)(struct mt6397_chip *chip); }; static const struct chip_data mt6323_core = { .cid_addr = MT6323_CID, .cid_shift = 0, + .cells = mt6323_devs, + .cell_size = ARRAY_SIZE(mt6323_devs), + .irq_init = mt6397_irq_init, }; static const struct chip_data mt6397_core = { .cid_addr = MT6397_CID, .cid_shift = 0, + .cells = mt6397_devs, + .cell_size = ARRAY_SIZE(mt6397_devs), + .irq_init = mt6397_irq_init, }; static int mt6397_probe(struct platform_device *pdev) { int ret; - unsigned int id; + unsigned int id = 0; struct mt6397_chip *pmic; const struct chip_data *pmic_core; @@ -154,29 +163,13 @@ static int mt6397_probe(struct platform_device *pdev) if (pmic->irq <= 0) return pmic->irq; - ret = mt6397_irq_init(pmic); + ret = pmic_core->irq_init(pmic); if (ret) return ret; - switch (pmic->chip_id) { - case MT6323_CHIP_ID: - ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, - mt6323_devs, ARRAY_SIZE(mt6323_devs), - NULL, 0, pmic->irq_domain); - break; - - case MT6391_CHIP_ID: - case MT6397_CHIP_ID: - ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, - mt6397_devs, ARRAY_SIZE(mt6397_devs), - NULL, 0, pmic->irq_domain); - break; - - default: - dev_err(&pdev->dev, "unsupported chip: %d\n", pmic->chip_id); - return -ENODEV; - } - + ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, + pmic_core->cells, pmic_core->cell_size, + NULL, 0, pmic->irq_domain); if (ret) { irq_domain_remove(pmic->irq_domain); dev_err(&pdev->dev, "failed to add child devices: %d\n", ret); -- cgit v1.2.3 From 6c3d5c97d260177bfe24b533e80df8500115be2f Mon Sep 17 00:00:00 2001 From: Hsin-Hsiung Wang Date: Tue, 21 Apr 2020 11:00:09 +0800 Subject: dt-bindings: mfd: Add compatible for the MediaTek MT6358 PMIC This adds compatible for the MediaTek MT6358 PMIC. Signed-off-by: Hsin-Hsiung Wang Reviewed-by: Rob Herring Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/mt6397.txt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/mt6397.txt b/Documentation/devicetree/bindings/mfd/mt6397.txt index a9b105ac00a8..0df4382afc20 100644 --- a/Documentation/devicetree/bindings/mfd/mt6397.txt +++ b/Documentation/devicetree/bindings/mfd/mt6397.txt @@ -18,24 +18,30 @@ See the following for pwarp node definitions: This document describes the binding for MFD device and its sub module. Required properties: -compatible: "mediatek,mt6397" or "mediatek,mt6323" +compatible: + "mediatek,mt6323" for PMIC MT6323 + "mediatek,mt6358" for PMIC MT6358 + "mediatek,mt6397" for PMIC MT6397 Optional subnodes: - rtc Required properties: Should be one of follows - compatible: "mediatek,mt6323-rtc" + - compatible: "mediatek,mt6358-rtc" - compatible: "mediatek,mt6397-rtc" For details, see ../rtc/rtc-mt6397.txt - regulators Required properties: - - compatible: "mediatek,mt6397-regulator" - see ../regulator/mt6397-regulator.txt - compatible: "mediatek,mt6323-regulator" see ../regulator/mt6323-regulator.txt + - compatible: "mediatek,mt6358-regulator" + see ../regulator/mt6358-regulator.txt + - compatible: "mediatek,mt6397-regulator" + see ../regulator/mt6397-regulator.txt - codec Required properties: - - compatible: "mediatek,mt6397-codec" + - compatible: "mediatek,mt6397-codec" or "mediatek,mt6358-sound" - clk Required properties: - compatible: "mediatek,mt6397-clk" -- cgit v1.2.3 From 2b91c28f2abd9471aac4aa6cd8de7896ef469153 Mon Sep 17 00:00:00 2001 From: Hsin-Hsiung Wang Date: Tue, 21 Apr 2020 11:00:10 +0800 Subject: mfd: Add support for the MediaTek MT6358 PMIC This adds support for the MediaTek MT6358 PMIC. This is a multifunction device with the following sub modules: - Regulator - RTC - Codec - Interrupt It is interfaced to the host controller using SPI interface by a proprietary hardware called PMIC wrapper or pwrap. MT6358 MFD is a child device of the pwrap. Signed-off-by: Hsin-Hsiung Wang Signed-off-by: Lee Jones --- drivers/mfd/Makefile | 2 +- drivers/mfd/mt6358-irq.c | 235 +++++++++++++++++++++++++++++ drivers/mfd/mt6397-core.c | 36 +++++ include/linux/mfd/mt6358/core.h | 158 ++++++++++++++++++++ include/linux/mfd/mt6358/registers.h | 282 +++++++++++++++++++++++++++++++++++ include/linux/mfd/mt6397/core.h | 3 + 6 files changed, 715 insertions(+), 1 deletion(-) create mode 100644 drivers/mfd/mt6358-irq.c create mode 100644 include/linux/mfd/mt6358/core.h create mode 100644 include/linux/mfd/mt6358/registers.h diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f935d10cbf0f..947032b63c64 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -239,7 +239,7 @@ obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o obj-$(CONFIG_INTEL_SOC_PMIC_BXTWC) += intel_soc_pmic_bxtwc.o obj-$(CONFIG_INTEL_SOC_PMIC_CHTWC) += intel_soc_pmic_chtwc.o obj-$(CONFIG_INTEL_SOC_PMIC_CHTDC_TI) += intel_soc_pmic_chtdc_ti.o -mt6397-objs := mt6397-core.o mt6397-irq.o +mt6397-objs := mt6397-core.o mt6397-irq.o mt6358-irq.o obj-$(CONFIG_MFD_MT6397) += mt6397.o obj-$(CONFIG_INTEL_SOC_PMIC_MRFLD) += intel_soc_pmic_mrfld.o diff --git a/drivers/mfd/mt6358-irq.c b/drivers/mfd/mt6358-irq.c new file mode 100644 index 000000000000..db734f2831ff --- /dev/null +++ b/drivers/mfd/mt6358-irq.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2020 MediaTek Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct irq_top_t mt6358_ints[] = { + MT6358_TOP_GEN(BUCK), + MT6358_TOP_GEN(LDO), + MT6358_TOP_GEN(PSC), + MT6358_TOP_GEN(SCK), + MT6358_TOP_GEN(BM), + MT6358_TOP_GEN(HK), + MT6358_TOP_GEN(AUD), + MT6358_TOP_GEN(MISC), +}; + +static void pmic_irq_enable(struct irq_data *data) +{ + unsigned int hwirq = irqd_to_hwirq(data); + struct mt6397_chip *chip = irq_data_get_irq_chip_data(data); + struct pmic_irq_data *irqd = chip->irq_data; + + irqd->enable_hwirq[hwirq] = true; +} + +static void pmic_irq_disable(struct irq_data *data) +{ + unsigned int hwirq = irqd_to_hwirq(data); + struct mt6397_chip *chip = irq_data_get_irq_chip_data(data); + struct pmic_irq_data *irqd = chip->irq_data; + + irqd->enable_hwirq[hwirq] = false; +} + +static void pmic_irq_lock(struct irq_data *data) +{ + struct mt6397_chip *chip = irq_data_get_irq_chip_data(data); + + mutex_lock(&chip->irqlock); +} + +static void pmic_irq_sync_unlock(struct irq_data *data) +{ + unsigned int i, top_gp, gp_offset, en_reg, int_regs, shift; + struct mt6397_chip *chip = irq_data_get_irq_chip_data(data); + struct pmic_irq_data *irqd = chip->irq_data; + + for (i = 0; i < irqd->num_pmic_irqs; i++) { + if (irqd->enable_hwirq[i] == irqd->cache_hwirq[i]) + continue; + + /* Find out the IRQ group */ + top_gp = 0; + while ((top_gp + 1) < irqd->num_top && + i >= mt6358_ints[top_gp + 1].hwirq_base) + top_gp++; + + /* Find the IRQ registers */ + gp_offset = i - mt6358_ints[top_gp].hwirq_base; + int_regs = gp_offset / MT6358_REG_WIDTH; + shift = gp_offset % MT6358_REG_WIDTH; + en_reg = mt6358_ints[top_gp].en_reg + + (mt6358_ints[top_gp].en_reg_shift * int_regs); + + regmap_update_bits(chip->regmap, en_reg, BIT(shift), + irqd->enable_hwirq[i] << shift); + + irqd->cache_hwirq[i] = irqd->enable_hwirq[i]; + } + mutex_unlock(&chip->irqlock); +} + +static struct irq_chip mt6358_irq_chip = { + .name = "mt6358-irq", + .flags = IRQCHIP_SKIP_SET_WAKE, + .irq_enable = pmic_irq_enable, + .irq_disable = pmic_irq_disable, + .irq_bus_lock = pmic_irq_lock, + .irq_bus_sync_unlock = pmic_irq_sync_unlock, +}; + +static void mt6358_irq_sp_handler(struct mt6397_chip *chip, + unsigned int top_gp) +{ + unsigned int irq_status, sta_reg, status; + unsigned int hwirq, virq; + int i, j, ret; + + for (i = 0; i < mt6358_ints[top_gp].num_int_regs; i++) { + sta_reg = mt6358_ints[top_gp].sta_reg + + mt6358_ints[top_gp].sta_reg_shift * i; + + ret = regmap_read(chip->regmap, sta_reg, &irq_status); + if (ret) { + dev_err(chip->dev, + "Failed to read IRQ status, ret=%d\n", ret); + return; + } + + if (!irq_status) + continue; + + status = irq_status; + do { + j = __ffs(status); + + hwirq = mt6358_ints[top_gp].hwirq_base + + MT6358_REG_WIDTH * i + j; + + virq = irq_find_mapping(chip->irq_domain, hwirq); + if (virq) + handle_nested_irq(virq); + + status &= ~BIT(j); + } while (status); + + regmap_write(chip->regmap, sta_reg, irq_status); + } +} + +static irqreturn_t mt6358_irq_handler(int irq, void *data) +{ + struct mt6397_chip *chip = data; + struct pmic_irq_data *mt6358_irq_data = chip->irq_data; + unsigned int bit, i, top_irq_status = 0; + int ret; + + ret = regmap_read(chip->regmap, + mt6358_irq_data->top_int_status_reg, + &top_irq_status); + if (ret) { + dev_err(chip->dev, + "Failed to read status from the device, ret=%d\n", ret); + return IRQ_NONE; + } + + for (i = 0; i < mt6358_irq_data->num_top; i++) { + bit = BIT(mt6358_ints[i].top_offset); + if (top_irq_status & bit) { + mt6358_irq_sp_handler(chip, i); + top_irq_status &= ~bit; + if (!top_irq_status) + break; + } + } + + return IRQ_HANDLED; +} + +static int pmic_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + struct mt6397_chip *mt6397 = d->host_data; + + irq_set_chip_data(irq, mt6397); + irq_set_chip_and_handler(irq, &mt6358_irq_chip, handle_level_irq); + irq_set_nested_thread(irq, 1); + irq_set_noprobe(irq); + + return 0; +} + +static const struct irq_domain_ops mt6358_irq_domain_ops = { + .map = pmic_irq_domain_map, + .xlate = irq_domain_xlate_twocell, +}; + +int mt6358_irq_init(struct mt6397_chip *chip) +{ + int i, j, ret; + struct pmic_irq_data *irqd; + + irqd = devm_kzalloc(chip->dev, sizeof(*irqd), GFP_KERNEL); + if (!irqd) + return -ENOMEM; + + chip->irq_data = irqd; + + mutex_init(&chip->irqlock); + irqd->top_int_status_reg = MT6358_TOP_INT_STATUS0; + irqd->num_pmic_irqs = MT6358_IRQ_NR; + irqd->num_top = ARRAY_SIZE(mt6358_ints); + + irqd->enable_hwirq = devm_kcalloc(chip->dev, + irqd->num_pmic_irqs, + sizeof(*irqd->enable_hwirq), + GFP_KERNEL); + if (!irqd->enable_hwirq) + return -ENOMEM; + + irqd->cache_hwirq = devm_kcalloc(chip->dev, + irqd->num_pmic_irqs, + sizeof(*irqd->cache_hwirq), + GFP_KERNEL); + if (!irqd->cache_hwirq) + return -ENOMEM; + + /* Disable all interrupts for initializing */ + for (i = 0; i < irqd->num_top; i++) { + for (j = 0; j < mt6358_ints[i].num_int_regs; j++) + regmap_write(chip->regmap, + mt6358_ints[i].en_reg + + mt6358_ints[i].en_reg_shift * j, 0); + } + + chip->irq_domain = irq_domain_add_linear(chip->dev->of_node, + irqd->num_pmic_irqs, + &mt6358_irq_domain_ops, chip); + if (!chip->irq_domain) { + dev_err(chip->dev, "Could not create IRQ domain\n"); + return -ENODEV; + } + + ret = devm_request_threaded_irq(chip->dev, chip->irq, NULL, + mt6358_irq_handler, IRQF_ONESHOT, + mt6358_irq_chip.name, chip); + if (ret) { + dev_err(chip->dev, "Failed to register IRQ=%d, ret=%d\n", + chip->irq, ret); + return ret; + } + + enable_irq_wake(chip->irq); + return ret; +} diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c index a313a720bb8b..f6cd8a660602 100644 --- a/drivers/mfd/mt6397-core.c +++ b/drivers/mfd/mt6397-core.c @@ -12,13 +12,18 @@ #include #include #include +#include #include #include +#include #include #define MT6323_RTC_BASE 0x8000 #define MT6323_RTC_SIZE 0x40 +#define MT6358_RTC_BASE 0x0588 +#define MT6358_RTC_SIZE 0x3c + #define MT6397_RTC_BASE 0xe000 #define MT6397_RTC_SIZE 0x3e @@ -30,6 +35,11 @@ static const struct resource mt6323_rtc_resources[] = { DEFINE_RES_IRQ(MT6323_IRQ_STATUS_RTC), }; +static const struct resource mt6358_rtc_resources[] = { + DEFINE_RES_MEM(MT6358_RTC_BASE, MT6358_RTC_SIZE), + DEFINE_RES_IRQ(MT6358_IRQ_RTC), +}; + static const struct resource mt6397_rtc_resources[] = { DEFINE_RES_MEM(MT6397_RTC_BASE, MT6397_RTC_SIZE), DEFINE_RES_IRQ(MT6397_IRQ_RTC), @@ -74,6 +84,21 @@ static const struct mfd_cell mt6323_devs[] = { }, }; +static const struct mfd_cell mt6358_devs[] = { + { + .name = "mt6358-regulator", + .of_compatible = "mediatek,mt6358-regulator" + }, { + .name = "mt6358-rtc", + .num_resources = ARRAY_SIZE(mt6358_rtc_resources), + .resources = mt6358_rtc_resources, + .of_compatible = "mediatek,mt6358-rtc", + }, { + .name = "mt6358-sound", + .of_compatible = "mediatek,mt6358-sound" + }, +}; + static const struct mfd_cell mt6397_devs[] = { { .name = "mt6397-rtc", @@ -116,6 +141,14 @@ static const struct chip_data mt6323_core = { .irq_init = mt6397_irq_init, }; +static const struct chip_data mt6358_core = { + .cid_addr = MT6358_SWCID, + .cid_shift = 8, + .cells = mt6358_devs, + .cell_size = ARRAY_SIZE(mt6358_devs), + .irq_init = mt6358_irq_init, +}; + static const struct chip_data mt6397_core = { .cid_addr = MT6397_CID, .cid_shift = 0, @@ -182,6 +215,9 @@ static const struct of_device_id mt6397_of_match[] = { { .compatible = "mediatek,mt6323", .data = &mt6323_core, + }, { + .compatible = "mediatek,mt6358", + .data = &mt6358_core, }, { .compatible = "mediatek,mt6397", .data = &mt6397_core, diff --git a/include/linux/mfd/mt6358/core.h b/include/linux/mfd/mt6358/core.h new file mode 100644 index 000000000000..c5a11b7458d4 --- /dev/null +++ b/include/linux/mfd/mt6358/core.h @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020 MediaTek Inc. + */ + +#ifndef __MFD_MT6358_CORE_H__ +#define __MFD_MT6358_CORE_H__ + +#define MT6358_REG_WIDTH 16 + +struct irq_top_t { + int hwirq_base; + unsigned int num_int_regs; + unsigned int num_int_bits; + unsigned int en_reg; + unsigned int en_reg_shift; + unsigned int sta_reg; + unsigned int sta_reg_shift; + unsigned int top_offset; +}; + +struct pmic_irq_data { + unsigned int num_top; + unsigned int num_pmic_irqs; + unsigned short top_int_status_reg; + bool *enable_hwirq; + bool *cache_hwirq; +}; + +enum mt6358_irq_top_status_shift { + MT6358_BUCK_TOP = 0, + MT6358_LDO_TOP, + MT6358_PSC_TOP, + MT6358_SCK_TOP, + MT6358_BM_TOP, + MT6358_HK_TOP, + MT6358_AUD_TOP, + MT6358_MISC_TOP, +}; + +enum mt6358_irq_numbers { + MT6358_IRQ_VPROC11_OC = 0, + MT6358_IRQ_VPROC12_OC, + MT6358_IRQ_VCORE_OC, + MT6358_IRQ_VGPU_OC, + MT6358_IRQ_VMODEM_OC, + MT6358_IRQ_VDRAM1_OC, + MT6358_IRQ_VS1_OC, + MT6358_IRQ_VS2_OC, + MT6358_IRQ_VPA_OC, + MT6358_IRQ_VCORE_PREOC, + MT6358_IRQ_VFE28_OC = 16, + MT6358_IRQ_VXO22_OC, + MT6358_IRQ_VRF18_OC, + MT6358_IRQ_VRF12_OC, + MT6358_IRQ_VEFUSE_OC, + MT6358_IRQ_VCN33_OC, + MT6358_IRQ_VCN28_OC, + MT6358_IRQ_VCN18_OC, + MT6358_IRQ_VCAMA1_OC, + MT6358_IRQ_VCAMA2_OC, + MT6358_IRQ_VCAMD_OC, + MT6358_IRQ_VCAMIO_OC, + MT6358_IRQ_VLDO28_OC, + MT6358_IRQ_VA12_OC, + MT6358_IRQ_VAUX18_OC, + MT6358_IRQ_VAUD28_OC, + MT6358_IRQ_VIO28_OC, + MT6358_IRQ_VIO18_OC, + MT6358_IRQ_VSRAM_PROC11_OC, + MT6358_IRQ_VSRAM_PROC12_OC, + MT6358_IRQ_VSRAM_OTHERS_OC, + MT6358_IRQ_VSRAM_GPU_OC, + MT6358_IRQ_VDRAM2_OC, + MT6358_IRQ_VMC_OC, + MT6358_IRQ_VMCH_OC, + MT6358_IRQ_VEMC_OC, + MT6358_IRQ_VSIM1_OC, + MT6358_IRQ_VSIM2_OC, + MT6358_IRQ_VIBR_OC, + MT6358_IRQ_VUSB_OC, + MT6358_IRQ_VBIF28_OC, + MT6358_IRQ_PWRKEY = 48, + MT6358_IRQ_HOMEKEY, + MT6358_IRQ_PWRKEY_R, + MT6358_IRQ_HOMEKEY_R, + MT6358_IRQ_NI_LBAT_INT, + MT6358_IRQ_CHRDET, + MT6358_IRQ_CHRDET_EDGE, + MT6358_IRQ_VCDT_HV_DET, + MT6358_IRQ_RTC = 64, + MT6358_IRQ_FG_BAT0_H = 80, + MT6358_IRQ_FG_BAT0_L, + MT6358_IRQ_FG_CUR_H, + MT6358_IRQ_FG_CUR_L, + MT6358_IRQ_FG_ZCV, + MT6358_IRQ_FG_BAT1_H, + MT6358_IRQ_FG_BAT1_L, + MT6358_IRQ_FG_N_CHARGE_L, + MT6358_IRQ_FG_IAVG_H, + MT6358_IRQ_FG_IAVG_L, + MT6358_IRQ_FG_TIME_H, + MT6358_IRQ_FG_DISCHARGE, + MT6358_IRQ_FG_CHARGE, + MT6358_IRQ_BATON_LV = 96, + MT6358_IRQ_BATON_HT, + MT6358_IRQ_BATON_BAT_IN, + MT6358_IRQ_BATON_BAT_OUT, + MT6358_IRQ_BIF, + MT6358_IRQ_BAT_H = 112, + MT6358_IRQ_BAT_L, + MT6358_IRQ_BAT2_H, + MT6358_IRQ_BAT2_L, + MT6358_IRQ_BAT_TEMP_H, + MT6358_IRQ_BAT_TEMP_L, + MT6358_IRQ_AUXADC_IMP, + MT6358_IRQ_NAG_C_DLTV, + MT6358_IRQ_AUDIO = 128, + MT6358_IRQ_ACCDET = 133, + MT6358_IRQ_ACCDET_EINT0, + MT6358_IRQ_ACCDET_EINT1, + MT6358_IRQ_SPI_CMD_ALERT = 144, + MT6358_IRQ_NR, +}; + +#define MT6358_IRQ_BUCK_BASE MT6358_IRQ_VPROC11_OC +#define MT6358_IRQ_LDO_BASE MT6358_IRQ_VFE28_OC +#define MT6358_IRQ_PSC_BASE MT6358_IRQ_PWRKEY +#define MT6358_IRQ_SCK_BASE MT6358_IRQ_RTC +#define MT6358_IRQ_BM_BASE MT6358_IRQ_FG_BAT0_H +#define MT6358_IRQ_HK_BASE MT6358_IRQ_BAT_H +#define MT6358_IRQ_AUD_BASE MT6358_IRQ_AUDIO +#define MT6358_IRQ_MISC_BASE MT6358_IRQ_SPI_CMD_ALERT + +#define MT6358_IRQ_BUCK_BITS (MT6358_IRQ_VCORE_PREOC - MT6358_IRQ_BUCK_BASE + 1) +#define MT6358_IRQ_LDO_BITS (MT6358_IRQ_VBIF28_OC - MT6358_IRQ_LDO_BASE + 1) +#define MT6358_IRQ_PSC_BITS (MT6358_IRQ_VCDT_HV_DET - MT6358_IRQ_PSC_BASE + 1) +#define MT6358_IRQ_SCK_BITS (MT6358_IRQ_RTC - MT6358_IRQ_SCK_BASE + 1) +#define MT6358_IRQ_BM_BITS (MT6358_IRQ_BIF - MT6358_IRQ_BM_BASE + 1) +#define MT6358_IRQ_HK_BITS (MT6358_IRQ_NAG_C_DLTV - MT6358_IRQ_HK_BASE + 1) +#define MT6358_IRQ_AUD_BITS (MT6358_IRQ_ACCDET_EINT1 - MT6358_IRQ_AUD_BASE + 1) +#define MT6358_IRQ_MISC_BITS \ + (MT6358_IRQ_SPI_CMD_ALERT - MT6358_IRQ_MISC_BASE + 1) + +#define MT6358_TOP_GEN(sp) \ +{ \ + .hwirq_base = MT6358_IRQ_##sp##_BASE, \ + .num_int_regs = \ + ((MT6358_IRQ_##sp##_BITS - 1) / MT6358_REG_WIDTH) + 1, \ + .num_int_bits = MT6358_IRQ_##sp##_BITS, \ + .en_reg = MT6358_##sp##_TOP_INT_CON0, \ + .en_reg_shift = 0x6, \ + .sta_reg = MT6358_##sp##_TOP_INT_STATUS0, \ + .sta_reg_shift = 0x2, \ + .top_offset = MT6358_##sp##_TOP, \ +} + +#endif /* __MFD_MT6358_CORE_H__ */ diff --git a/include/linux/mfd/mt6358/registers.h b/include/linux/mfd/mt6358/registers.h new file mode 100644 index 000000000000..2ad0b312aa28 --- /dev/null +++ b/include/linux/mfd/mt6358/registers.h @@ -0,0 +1,282 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020 MediaTek Inc. + */ + +#ifndef __MFD_MT6358_REGISTERS_H__ +#define __MFD_MT6358_REGISTERS_H__ + +/* PMIC Registers */ +#define MT6358_SWCID 0xa +#define MT6358_MISC_TOP_INT_CON0 0x188 +#define MT6358_MISC_TOP_INT_STATUS0 0x194 +#define MT6358_TOP_INT_STATUS0 0x19e +#define MT6358_SCK_TOP_INT_CON0 0x52e +#define MT6358_SCK_TOP_INT_STATUS0 0x53a +#define MT6358_EOSC_CALI_CON0 0x540 +#define MT6358_EOSC_CALI_CON1 0x542 +#define MT6358_RTC_MIX_CON0 0x544 +#define MT6358_RTC_MIX_CON1 0x546 +#define MT6358_RTC_MIX_CON2 0x548 +#define MT6358_RTC_DSN_ID 0x580 +#define MT6358_RTC_DSN_REV0 0x582 +#define MT6358_RTC_DBI 0x584 +#define MT6358_RTC_DXI 0x586 +#define MT6358_RTC_BBPU 0x588 +#define MT6358_RTC_IRQ_STA 0x58a +#define MT6358_RTC_IRQ_EN 0x58c +#define MT6358_RTC_CII_EN 0x58e +#define MT6358_RTC_AL_MASK 0x590 +#define MT6358_RTC_TC_SEC 0x592 +#define MT6358_RTC_TC_MIN 0x594 +#define MT6358_RTC_TC_HOU 0x596 +#define MT6358_RTC_TC_DOM 0x598 +#define MT6358_RTC_TC_DOW 0x59a +#define MT6358_RTC_TC_MTH 0x59c +#define MT6358_RTC_TC_YEA 0x59e +#define MT6358_RTC_AL_SEC 0x5a0 +#define MT6358_RTC_AL_MIN 0x5a2 +#define MT6358_RTC_AL_HOU 0x5a4 +#define MT6358_RTC_AL_DOM 0x5a6 +#define MT6358_RTC_AL_DOW 0x5a8 +#define MT6358_RTC_AL_MTH 0x5aa +#define MT6358_RTC_AL_YEA 0x5ac +#define MT6358_RTC_OSC32CON 0x5ae +#define MT6358_RTC_POWERKEY1 0x5b0 +#define MT6358_RTC_POWERKEY2 0x5b2 +#define MT6358_RTC_PDN1 0x5b4 +#define MT6358_RTC_PDN2 0x5b6 +#define MT6358_RTC_SPAR0 0x5b8 +#define MT6358_RTC_SPAR1 0x5ba +#define MT6358_RTC_PROT 0x5bc +#define MT6358_RTC_DIFF 0x5be +#define MT6358_RTC_CALI 0x5c0 +#define MT6358_RTC_WRTGR 0x5c2 +#define MT6358_RTC_CON 0x5c4 +#define MT6358_RTC_SEC_CTRL 0x5c6 +#define MT6358_RTC_INT_CNT 0x5c8 +#define MT6358_RTC_SEC_DAT0 0x5ca +#define MT6358_RTC_SEC_DAT1 0x5cc +#define MT6358_RTC_SEC_DAT2 0x5ce +#define MT6358_RTC_SEC_DSN_ID 0x600 +#define MT6358_RTC_SEC_DSN_REV0 0x602 +#define MT6358_RTC_SEC_DBI 0x604 +#define MT6358_RTC_SEC_DXI 0x606 +#define MT6358_RTC_TC_SEC_SEC 0x608 +#define MT6358_RTC_TC_MIN_SEC 0x60a +#define MT6358_RTC_TC_HOU_SEC 0x60c +#define MT6358_RTC_TC_DOM_SEC 0x60e +#define MT6358_RTC_TC_DOW_SEC 0x610 +#define MT6358_RTC_TC_MTH_SEC 0x612 +#define MT6358_RTC_TC_YEA_SEC 0x614 +#define MT6358_RTC_SEC_CK_PDN 0x616 +#define MT6358_RTC_SEC_WRTGR 0x618 +#define MT6358_PSC_TOP_INT_CON0 0x910 +#define MT6358_PSC_TOP_INT_STATUS0 0x91c +#define MT6358_BM_TOP_INT_CON0 0xc32 +#define MT6358_BM_TOP_INT_CON1 0xc38 +#define MT6358_BM_TOP_INT_STATUS0 0xc4a +#define MT6358_BM_TOP_INT_STATUS1 0xc4c +#define MT6358_HK_TOP_INT_CON0 0xf92 +#define MT6358_HK_TOP_INT_STATUS0 0xf9e +#define MT6358_BUCK_TOP_INT_CON0 0x1318 +#define MT6358_BUCK_TOP_INT_STATUS0 0x1324 +#define MT6358_BUCK_VPROC11_CON0 0x1388 +#define MT6358_BUCK_VPROC11_DBG0 0x139e +#define MT6358_BUCK_VPROC11_DBG1 0x13a0 +#define MT6358_BUCK_VPROC11_ELR0 0x13a6 +#define MT6358_BUCK_VPROC12_CON0 0x1408 +#define MT6358_BUCK_VPROC12_DBG0 0x141e +#define MT6358_BUCK_VPROC12_DBG1 0x1420 +#define MT6358_BUCK_VPROC12_ELR0 0x1426 +#define MT6358_BUCK_VCORE_CON0 0x1488 +#define MT6358_BUCK_VCORE_DBG0 0x149e +#define MT6358_BUCK_VCORE_DBG1