* [PATCH v2 2/4] mfd: mt6360: implement i2c R/W with CRC
2020-06-19 11:53 [PATCH v2 0/4] dt-bindings: mfd: Add bindings for the Mediatek MT6360 Gene Chen
2020-06-19 11:53 ` [PATCH v2 1/4] dt-bindings: mfd: Add bindings for the Mediatek MT6360 PMIC Gene Chen
@ 2020-06-19 11:53 ` Gene Chen
2020-07-01 7:16 ` Lee Jones
2020-06-19 11:53 ` [PATCH v2 3/4] iio: adc: mt6360: Add ADC driver for MT6360 Gene Chen
` (2 subsequent siblings)
4 siblings, 1 reply; 10+ messages in thread
From: Gene Chen @ 2020-06-19 11:53 UTC (permalink / raw)
To: lee.jones, jic23, jacek.anaszewski, pavel, matthias.bgg
Cc: gene_chen, lars, lgirdwood, linux-kernel, cy_huang,
benjamin.chao, broonie, linux-mediatek, dmurphy, pmeerw,
knaack.h, linux-arm-kernel, shufan_lee
From: Gene Chen <gene_chen@richtek.com>
imlement i2c R/W with CRC when access sub-device PMIC and LDO part
Signed-off-by: Gene Chen <gene_chen@richtek.com>
---
drivers/mfd/Kconfig | 1 +
drivers/mfd/mt6360-core.c | 541 +++++++++++++++++++++++++++++----------------
include/linux/mfd/mt6360.h | 240 --------------------
3 files changed, 357 insertions(+), 425 deletions(-)
delete mode 100644 include/linux/mfd/mt6360.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index a37d7d1..0684ddc 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -913,6 +913,7 @@ config MFD_MT6360
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
+ select CRC8
depends on I2C
help
Say Y here to enable MT6360 PMU/PMIC/LDO functional support.
diff --git a/drivers/mfd/mt6360-core.c b/drivers/mfd/mt6360-core.c
index e9cacc2..00e3cb5 100644
--- a/drivers/mfd/mt6360-core.c
+++ b/drivers/mfd/mt6360-core.c
@@ -5,122 +5,160 @@
* Author: Gene Chen <gene_chen@richtek.com>
*/
+#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/i2c.h>
-#include <linux/init.h>
+#include <linux/crc8.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
#include <linux/interrupt.h>
-#include <linux/kernel.h>
#include <linux/mfd/core.h>
-#include <linux/module.h>
-#include <linux/of_irq.h>
-#include <linux/of_platform.h>
-#include <linux/version.h>
-#include <linux/mfd/mt6360.h>
+enum {
+ MT6360_SLAVE_TCPC = 0,
+ MT6360_SLAVE_PMIC,
+ MT6360_SLAVE_LDO,
+ MT6360_SLAVE_PMU,
+ MT6360_SLAVE_MAX,
+};
+
+struct mt6360_data {
+ struct i2c_client *i2c[MT6360_SLAVE_MAX];
+ struct device *dev;
+ struct regmap *regmap;
+ struct regmap_irq_chip_data *irq_data;
+ unsigned int chip_rev;
+ u8 crc8_tbl[CRC8_TABLE_SIZE];
+};
+
+#define MT6360_PMU_SLAVEID 0x34
+#define MT6360_PMIC_SLAVEID 0x1A
+#define MT6360_LDO_SLAVEID 0x64
+#define MT6360_TCPC_SLAVEID 0x4E
+
+#define MT6360_REG_TCPCSTART 0x00
+#define MT6360_REG_TCPCEND 0xFF
+#define MT6360_REG_PMICSTART 0x100
+#define MT6360_REG_PMICEND 0x13B
+#define MT6360_REG_LDOSTART 0x200
+#define MT6360_REG_LDOEND 0x21C
+#define MT6360_REG_PMUSTART 0x300
+#define MT6360_REG_PMU_DEVINFO 0x300
+#define MT6360_REG_PMU_CHGIRQ1 0x3D0
+#define MT6360_REG_PMU_CHGMASK1 0x3F0
+#define MT6360_REG_PMUEND 0x3FF
+
+/* from 0x3D0 to 0x3DF */
+#define MT6360_IRQ_REGNUM 16
+
+#define MT6360_CHIPVEN_MASK 0xF0
+#define MT6360_CHIPVEN_VAL 0x50
+#define MT6360_CHIPREV_MASK 0x0F
/* reg 0 -> 0 ~ 7 */
-#define MT6360_CHG_TREG_EVT (4)
-#define MT6360_CHG_AICR_EVT (5)
-#define MT6360_CHG_MIVR_EVT (6)
-#define MT6360_PWR_RDY_EVT (7)
+#define MT6360_CHG_TREG_EVT 4
+#define MT6360_CHG_AICR_EVT 5
+#define MT6360_CHG_MIVR_EVT 6
+#define MT6360_PWR_RDY_EVT 7
/* REG 1 -> 8 ~ 15 */
-#define MT6360_CHG_BATSYSUV_EVT (9)
-#define MT6360_FLED_CHG_VINOVP_EVT (11)
-#define MT6360_CHG_VSYSUV_EVT (12)
-#define MT6360_CHG_VSYSOV_EVT (13)
-#define MT6360_CHG_VBATOV_EVT (14)
-#define MT6360_CHG_VBUSOV_EVT (15)
+#define MT6360_CHG_BATSYSUV_EVT 9
+#define MT6360_FLED_CHG_VINOVP_EVT 11
+#define MT6360_CHG_VSYSUV_EVT 12
+#define MT6360_CHG_VSYSOV_EVT 13
+#define MT6360_CHG_VBATOV_EVT 14
+#define MT6360_CHG_VBUSOV_EVT 15
/* REG 2 -> 16 ~ 23 */
/* REG 3 -> 24 ~ 31 */
-#define MT6360_WD_PMU_DET (25)
-#define MT6360_WD_PMU_DONE (26)
-#define MT6360_CHG_TMRI (27)
-#define MT6360_CHG_ADPBADI (29)
-#define MT6360_CHG_RVPI (30)
-#define MT6360_OTPI (31)
+#define MT6360_WD_PMU_DET 25
+#define MT6360_WD_PMU_DONE 26
+#define MT6360_CHG_TMRI 27
+#define MT6360_CHG_ADPBADI 29
+#define MT6360_CHG_RVPI 30
+#define MT6360_OTPI 31
/* REG 4 -> 32 ~ 39 */
-#define MT6360_CHG_AICCMEASL (32)
-#define MT6360_CHGDET_DONEI (34)
-#define MT6360_WDTMRI (35)
-#define MT6360_SSFINISHI (36)
-#define MT6360_CHG_RECHGI (37)
-#define MT6360_CHG_TERMI (38)
-#define MT6360_CHG_IEOCI (39)
+#define MT6360_CHG_AICCMEASL 32
+#define MT6360_CHGDET_DONEI 34
+#define MT6360_WDTMRI 35
+#define MT6360_SSFINISHI 36
+#define MT6360_CHG_RECHGI 37
+#define MT6360_CHG_TERMI 38
+#define MT6360_CHG_IEOCI 39
/* REG 5 -> 40 ~ 47 */
-#define MT6360_PUMPX_DONEI (40)
-#define MT6360_BAT_OVP_ADC_EVT (41)
-#define MT6360_TYPEC_OTP_EVT (42)
-#define MT6360_ADC_WAKEUP_EVT (43)
-#define MT6360_ADC_DONEI (44)
-#define MT6360_BST_BATUVI (45)
-#define MT6360_BST_VBUSOVI (46)
-#define MT6360_BST_OLPI (47)
+#define MT6360_PUMPX_DONEI 40
+#define MT6360_BAT_OVP_ADC_EVT 41
+#define MT6360_TYPEC_OTP_EVT 42
+#define MT6360_ADC_WAKEUP_EVT 43
+#define MT6360_ADC_DONEI 44
+#define MT6360_BST_BATUVI 45
+#define MT6360_BST_VBUSOVI 46
+#define MT6360_BST_OLPI 47
/* REG 6 -> 48 ~ 55 */
-#define MT6360_ATTACH_I (48)
-#define MT6360_DETACH_I (49)
-#define MT6360_QC30_STPDONE (51)
-#define MT6360_QC_VBUSDET_DONE (52)
-#define MT6360_HVDCP_DET (53)
-#define MT6360_CHGDETI (54)
-#define MT6360_DCDTI (55)
+#define MT6360_ATTACH_I 48
+#define MT6360_DETACH_I 49
+#define MT6360_QC30_STPDONE 51
+#define MT6360_QC_VBUSDET_DONE 52
+#define MT6360_HVDCP_DET 53
+#define MT6360_CHGDETI 54
+#define MT6360_DCDTI 55
/* REG 7 -> 56 ~ 63 */
-#define MT6360_FOD_DONE_EVT (56)
-#define MT6360_FOD_OV_EVT (57)
-#define MT6360_CHRDET_UVP_EVT (58)
-#define MT6360_CHRDET_OVP_EVT (59)
-#define MT6360_CHRDET_EXT_EVT (60)
-#define MT6360_FOD_LR_EVT (61)
-#define MT6360_FOD_HR_EVT (62)
-#define MT6360_FOD_DISCHG_FAIL_EVT (63)
+#define MT6360_FOD_DONE_EVT 56
+#define MT6360_FOD_OV_EVT 57
+#define MT6360_CHRDET_UVP_EVT 58
+#define MT6360_CHRDET_OVP_EVT 59
+#define MT6360_CHRDET_EXT_EVT 60
+#define MT6360_FOD_LR_EVT 61
+#define MT6360_FOD_HR_EVT 62
+#define MT6360_FOD_DISCHG_FAIL_EVT 63
/* REG 8 -> 64 ~ 71 */
-#define MT6360_USBID_EVT (64)
-#define MT6360_APWDTRST_EVT (65)
-#define MT6360_EN_EVT (66)
-#define MT6360_QONB_RST_EVT (67)
-#define MT6360_MRSTB_EVT (68)
-#define MT6360_OTP_EVT (69)
-#define MT6360_VDDAOV_EVT (70)
-#define MT6360_SYSUV_EVT (71)
+#define MT6360_USBID_EVT 64
+#define MT6360_APWDTRST_EVT 65
+#define MT6360_EN_EVT 66
+#define MT6360_QONB_RST_EVT 67
+#define MT6360_MRSTB_EVT 68
+#define MT6360_OTP_EVT 69
+#define MT6360_VDDAOV_EVT 70
+#define MT6360_SYSUV_EVT 71
/* REG 9 -> 72 ~ 79 */
-#define MT6360_FLED_STRBPIN_EVT (72)
-#define MT6360_FLED_TORPIN_EVT (73)
-#define MT6360_FLED_TX_EVT (74)
-#define MT6360_FLED_LVF_EVT (75)
-#define MT6360_FLED2_SHORT_EVT (78)
-#define MT6360_FLED1_SHORT_EVT (79)
+#define MT6360_FLED_STRBPIN_EVT 72
+#define MT6360_FLED_TORPIN_EVT 73
+#define MT6360_FLED_TX_EVT 74
+#define MT6360_FLED_LVF_EVT 75
+#define MT6360_FLED2_SHORT_EVT 78
+#define MT6360_FLED1_SHORT_EVT 79
/* REG 10 -> 80 ~ 87 */
-#define MT6360_FLED2_STRB_EVT (80)
-#define MT6360_FLED1_STRB_EVT (81)
-#define MT6360_FLED2_STRB_TO_EVT (82)
-#define MT6360_FLED1_STRB_TO_EVT (83)
-#define MT6360_FLED2_TOR_EVT (84)
-#define MT6360_FLED1_TOR_EVT (85)
+#define MT6360_FLED2_STRB_EVT 80
+#define MT6360_FLED1_STRB_EVT 81
+#define MT6360_FLED2_STRB_TO_EVT 82
+#define MT6360_FLED1_STRB_TO_EVT 83
+#define MT6360_FLED2_TOR_EVT 84
+#define MT6360_FLED1_TOR_EVT 85
/* REG 11 -> 88 ~ 95 */
/* REG 12 -> 96 ~ 103 */
-#define MT6360_BUCK1_PGB_EVT (96)
-#define MT6360_BUCK1_OC_EVT (100)
-#define MT6360_BUCK1_OV_EVT (101)
-#define MT6360_BUCK1_UV_EVT (102)
+#define MT6360_BUCK1_PGB_EVT 96
+#define MT6360_BUCK1_OC_EVT 100
+#define MT6360_BUCK1_OV_EVT 101
+#define MT6360_BUCK1_UV_EVT 102
/* REG 13 -> 104 ~ 111 */
-#define MT6360_BUCK2_PGB_EVT (104)
-#define MT6360_BUCK2_OC_EVT (108)
-#define MT6360_BUCK2_OV_EVT (109)
-#define MT6360_BUCK2_UV_EVT (110)
+#define MT6360_BUCK2_PGB_EVT 104
+#define MT6360_BUCK2_OC_EVT 108
+#define MT6360_BUCK2_OV_EVT 109
+#define MT6360_BUCK2_UV_EVT 110
/* REG 14 -> 112 ~ 119 */
-#define MT6360_LDO1_OC_EVT (113)
-#define MT6360_LDO2_OC_EVT (114)
-#define MT6360_LDO3_OC_EVT (115)
-#define MT6360_LDO5_OC_EVT (117)
-#define MT6360_LDO6_OC_EVT (118)
-#define MT6360_LDO7_OC_EVT (119)
+#define MT6360_LDO1_OC_EVT 113
+#define MT6360_LDO2_OC_EVT 114
+#define MT6360_LDO3_OC_EVT 115
+#define MT6360_LDO5_OC_EVT 117
+#define MT6360_LDO6_OC_EVT 118
+#define MT6360_LDO7_OC_EVT 119
/* REG 15 -> 120 ~ 127 */
-#define MT6360_LDO1_PGB_EVT (121)
-#define MT6360_LDO2_PGB_EVT (122)
-#define MT6360_LDO3_PGB_EVT (123)
-#define MT6360_LDO5_PGB_EVT (125)
-#define MT6360_LDO6_PGB_EVT (126)
-#define MT6360_LDO7_PGB_EVT (127)
-
-static const struct regmap_irq mt6360_pmu_irqs[] = {
+#define MT6360_LDO1_PGB_EVT 121
+#define MT6360_LDO2_PGB_EVT 122
+#define MT6360_LDO3_PGB_EVT 123
+#define MT6360_LDO5_PGB_EVT 125
+#define MT6360_LDO6_PGB_EVT 126
+#define MT6360_LDO7_PGB_EVT 127
+
+static const struct regmap_irq mt6360_irqs[] = {
REGMAP_IRQ_REG_LINE(MT6360_CHG_TREG_EVT, 8),
REGMAP_IRQ_REG_LINE(MT6360_CHG_AICR_EVT, 8),
REGMAP_IRQ_REG_LINE(MT6360_CHG_MIVR_EVT, 8),
@@ -209,30 +247,16 @@ static const struct regmap_irq mt6360_pmu_irqs[] = {
REGMAP_IRQ_REG_LINE(MT6360_LDO7_PGB_EVT, 8),
};
-static int mt6360_pmu_handle_post_irq(void *irq_drv_data)
-{
- struct mt6360_pmu_data *mpd = irq_drv_data;
-
- return regmap_update_bits(mpd->regmap,
- MT6360_PMU_IRQ_SET, MT6360_IRQ_RETRIG, MT6360_IRQ_RETRIG);
-}
-
-static struct regmap_irq_chip mt6360_pmu_irq_chip = {
- .irqs = mt6360_pmu_irqs,
- .num_irqs = ARRAY_SIZE(mt6360_pmu_irqs),
- .num_regs = MT6360_PMU_IRQ_REGNUM,
- .mask_base = MT6360_PMU_CHG_MASK1,
- .status_base = MT6360_PMU_CHG_IRQ1,
- .ack_base = MT6360_PMU_CHG_IRQ1,
+static const struct regmap_irq_chip mt6360_irq_chip = {
+ .name = "mt6360_irqs",
+ .irqs = mt6360_irqs,
+ .num_irqs = ARRAY_SIZE(mt6360_irqs),
+ .num_regs = MT6360_IRQ_REGNUM,
+ .mask_base = MT6360_REG_PMU_CHGMASK1,
+ .status_base = MT6360_REG_PMU_CHGIRQ1,
+ .ack_base = MT6360_REG_PMU_CHGIRQ1,
.init_ack_masked = true,
.use_ack = true,
- .handle_post_irq = mt6360_pmu_handle_post_irq,
-};
-
-static const struct regmap_config mt6360_pmu_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = MT6360_PMU_MAXREG,
};
static const struct resource mt6360_adc_resources[] = {
@@ -266,7 +290,7 @@ static const struct resource mt6360_led_resources[] = {
DEFINE_RES_IRQ_NAMED(MT6360_FLED1_STRB_TO_EVT, "fled1_strb_to_evt"),
};
-static const struct resource mt6360_pmic_resources[] = {
+static const struct resource mt6360_regulator_resources[] = {
DEFINE_RES_IRQ_NAMED(MT6360_BUCK1_PGB_EVT, "buck1_pgb_evt"),
DEFINE_RES_IRQ_NAMED(MT6360_BUCK1_OC_EVT, "buck1_oc_evt"),
DEFINE_RES_IRQ_NAMED(MT6360_BUCK1_OV_EVT, "buck1_ov_evt"),
@@ -279,9 +303,6 @@ static const struct resource mt6360_pmic_resources[] = {
DEFINE_RES_IRQ_NAMED(MT6360_LDO7_OC_EVT, "ldo7_oc_evt"),
DEFINE_RES_IRQ_NAMED(MT6360_LDO6_PGB_EVT, "ldo6_pgb_evt"),
DEFINE_RES_IRQ_NAMED(MT6360_LDO7_PGB_EVT, "ldo7_pgb_evt"),
-};
-
-static const struct resource mt6360_ldo_resources[] = {
DEFINE_RES_IRQ_NAMED(MT6360_LDO1_OC_EVT, "ldo1_oc_evt"),
DEFINE_RES_IRQ_NAMED(MT6360_LDO2_OC_EVT, "ldo2_oc_evt"),
DEFINE_RES_IRQ_NAMED(MT6360_LDO3_OC_EVT, "ldo3_oc_evt"),
@@ -293,94 +314,245 @@ static const struct resource mt6360_ldo_resources[] = {
};
static const struct mfd_cell mt6360_devs[] = {
- OF_MFD_CELL("mt6360_adc", mt6360_adc_resources,
- NULL, 0, 0, "mediatek,mt6360_adc"),
- OF_MFD_CELL("mt6360_chg", mt6360_chg_resources,
- NULL, 0, 0, "mediatek,mt6360_chg"),
- OF_MFD_CELL("mt6360_led", mt6360_led_resources,
- NULL, 0, 0, "mediatek,mt6360_led"),
- OF_MFD_CELL("mt6360_pmic", mt6360_pmic_resources,
- NULL, 0, 0, "mediatek,mt6360_pmic"),
- OF_MFD_CELL("mt6360_ldo", mt6360_ldo_resources,
- NULL, 0, 0, "mediatek,mt6360_ldo"),
- OF_MFD_CELL("mt6360_tcpc", NULL,
- NULL, 0, 0, "mediatek,mt6360_tcpc"),
+ OF_MFD_CELL("mt6360-adc", mt6360_adc_resources,
+ NULL, 0, 0, "mediatek,mt6360-adc"),
+ OF_MFD_CELL("mt6360-chg", mt6360_chg_resources,
+ NULL, 0, 0, "mediatek,mt6360-chg"),
+ OF_MFD_CELL("mt6360-led", mt6360_led_resources,
+ NULL, 0, 0, "mediatek,mt6360-led"),
+ OF_MFD_CELL("mt6360-regulator", mt6360_regulator_resources,
+ NULL, 0, 0, "mediatek,mt6360-regulator"),
+ OF_MFD_CELL("mt6360-tcpc", NULL,
+ NULL, 0, 0, "mediatek,mt6360-tcpc"),
};
-static const unsigned short mt6360_slave_addr[MT6360_SLAVE_MAX] = {
- MT6360_PMU_SLAVEID,
+static int mt6360_check_vendor_info(struct mt6360_data *data)
+{
+ u32 info;
+ int ret;
+
+ ret = regmap_read(data->regmap, MT6360_REG_PMU_DEVINFO, &info);
+ if (ret < 0)
+ return ret;
+
+ if ((info & MT6360_CHIPVEN_MASK) != MT6360_CHIPVEN_VAL)
+ return -ENODEV;
+
+ data->chip_rev = info & MT6360_CHIPREV_MASK;
+
+ return 0;
+}
+
+static const u16 mt6360_slave_addrs[MT6360_SLAVE_MAX] = {
+ MT6360_TCPC_SLAVEID,
MT6360_PMIC_SLAVEID,
MT6360_LDO_SLAVEID,
- MT6360_TCPC_SLAVEID,
+ MT6360_PMU_SLAVEID,
};
-static int mt6360_pmu_probe(struct i2c_client *client)
+static int mt6360_xlate_pmicldo_addr(u8 *addr, int rw_size)
{
- struct mt6360_pmu_data *mpd;
- unsigned int reg_data;
- int i, ret;
+ u8 flags[4] = { 0x00, 0x40, 0x80, 0xc0 };
+
+ if (rw_size < 1 || rw_size > 4)
+ return -EINVAL;
- mpd = devm_kzalloc(&client->dev, sizeof(*mpd), GFP_KERNEL);
- if (!mpd)
+ *addr &= 0x3f;
+ *addr |= flags[rw_size - 1];
+
+ return 0;
+}
+
+static int mt6360_regmap_read(void *context, const void *reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ struct mt6360_data *data = context;
+ u8 bank = *(u8 *)reg, reg_addr = *(u8 *)(reg + 1);
+ struct i2c_client *i2c = data->i2c[bank];
+ bool crc_needed = false;
+ u8 *buf;
+ /* first two is i2c_addr + reg_addr , last is crc8 */
+ int alloc_size = 2 + val_size + 1, read_size = val_size;
+ u8 crc;
+ int ret;
+
+ if (bank == MT6360_SLAVE_PMIC || bank == MT6360_SLAVE_LDO) {
+ crc_needed = true;
+ ret = mt6360_xlate_pmicldo_addr(®_addr, val_size);
+ if (ret < 0)
+ return ret;
+ read_size += 1;
+ }
+
+ buf = kzalloc(alloc_size, GFP_KERNEL);
+ if (!buf)
return -ENOMEM;
- mpd->dev = &client->dev;
- i2c_set_clientdata(client, mpd);
+ /* 7 bit slave addr + read bit */
+ buf[0] = ((i2c->addr & 0x7f) << 1) + 1;
+ buf[1] = reg_addr;
- mpd->regmap = devm_regmap_init_i2c(client, &mt6360_pmu_regmap_config);
- if (IS_ERR(mpd->regmap)) {
- dev_err(&client->dev, "Failed to register regmap\n");
- return PTR_ERR(mpd->regmap);
+ ret = i2c_smbus_read_i2c_block_data(i2c, reg_addr, read_size, buf + 2);
+
+ if (ret == read_size) {
+ memcpy(val, buf + 2, val_size);
+ if (crc_needed) {
+ crc = crc8(data->crc8_tbl, buf, val_size + 2, 0);
+ if (crc != buf[val_size + 2])
+ ret = -EIO;
+ }
}
- ret = regmap_read(mpd->regmap, MT6360_PMU_DEV_INFO, ®_data);
- if (ret) {
- dev_err(&client->dev, "Device not found\n");
+ kfree(buf);
+
+ if (ret < 0)
return ret;
+ else if (ret != read_size)
+ return -EIO;
+
+ return 0;
+}
+
+static int mt6360_regmap_write(void *context, const void *val, size_t val_size)
+{
+ struct mt6360_data *data = context;
+ u8 bank = *(u8 *)val, reg_addr = *(u8 *)(val + 1);
+ struct i2c_client *i2c = data->i2c[bank];
+ bool crc_needed = false;
+ u8 *buf;
+ /* first two is i2c_addr + reg_addr , last crc8 + dummy */
+ int alloc_size = 2 + val_size + 2, write_size = val_size - 2;
+ int ret;
+
+ if (bank == MT6360_SLAVE_PMIC || bank == MT6360_SLAVE_LDO) {
+ crc_needed = true;
+ ret = mt6360_xlate_pmicldo_addr(®_addr, val_size - 2);
+ if (ret < 0)
+ return ret;
}
- mpd->chip_rev = reg_data & CHIP_REV_MASK;
- if (mpd->chip_rev != CHIP_VEN_MT6360) {
- dev_err(&client->dev, "Device not supported\n");
- return -ENODEV;
+ buf = kzalloc(alloc_size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* 7 bit slave addr + write bit */
+ buf[0] = ((i2c->addr & 0x7f) << 1);
+ buf[1] = reg_addr;
+ /* val need to minus regaddr 16bit */
+ memcpy(buf + 2, val + 2, write_size);
+
+ if (crc_needed) {
+ buf[val_size] = crc8(data->crc8_tbl, buf, val_size, 0);
+ write_size += 2;
}
- mt6360_pmu_irq_chip.irq_drv_data = mpd;
- ret = devm_regmap_add_irq_chip(&client->dev, mpd->regmap, client->irq,
- IRQF_TRIGGER_FALLING, 0,
- &mt6360_pmu_irq_chip, &mpd->irq_data);
- if (ret) {
- dev_err(&client->dev, "Failed to add Regmap IRQ Chip\n");
+ ret = i2c_smbus_write_i2c_block_data(i2c,
+ reg_addr, write_size, buf + 2);
+
+ kfree(buf);
+
+ if (ret < 0)
return ret;
+
+ return 0;
+}
+
+static const struct regmap_bus mt6360_regmap_bus = {
+ .read = mt6360_regmap_read,
+ .write = mt6360_regmap_write,
+
+ /* due to pmic and ldo crc access size limit */
+ .max_raw_read = 4,
+ .max_raw_write = 4,
+};
+
+static bool mt6360_is_readwrite_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MT6360_REG_TCPCSTART ... MT6360_REG_TCPCEND:
+ case MT6360_REG_PMICSTART ... MT6360_REG_PMICEND:
+ case MT6360_REG_LDOSTART ... MT6360_REG_LDOEND:
+ fallthrough;
+ case MT6360_REG_PMUSTART ... MT6360_REG_PMUEND:
+ return true;
}
- mpd->i2c[0] = client;
- for (i = 1; i < MT6360_SLAVE_MAX; i++) {
- mpd->i2c[i] = devm_i2c_new_dummy_device(&client->dev,
- client->adapter,
- mt6360_slave_addr[i]);
- if (IS_ERR(mpd->i2c[i])) {
+ return false;
+}
+
+static const struct regmap_config mt6360_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+
+ /* bank1:tcpc, bank2:pmic, bank3:ldo, bank4:pmu */
+ .max_register = MT6360_REG_PMUEND,
+ .writeable_reg = mt6360_is_readwrite_reg,
+ .readable_reg = mt6360_is_readwrite_reg,
+};
+
+static int mt6360_probe(struct i2c_client *client)
+{
+ struct mt6360_data *data;
+ int i, ret;
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = &client->dev;
+ crc8_populate_msb(data->crc8_tbl, 0x7);
+
+ for (i = 0; i < MT6360_SLAVE_PMU; i++) {
+ data->i2c[i] = devm_i2c_new_dummy_device(&client->dev,
+ client->adapter,
+ mt6360_slave_addrs[i]);
+ if (IS_ERR(data->i2c[i])) {
dev_err(&client->dev,
"Failed to get new dummy I2C device for address 0x%x",
- mt6360_slave_addr[i]);
- return PTR_ERR(mpd->i2c[i]);
+ mt6360_slave_addrs[i]);
+ return PTR_ERR(data->i2c[i]);
}
- i2c_set_clientdata(mpd->i2c[i], mpd);
+ }
+ data->i2c[MT6360_SLAVE_PMU] = client;
+
+ data->regmap = devm_regmap_init(&client->dev, &mt6360_regmap_bus,
+ data, &mt6360_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ dev_err(&client->dev, "Failed to register regmap\n");
+ return PTR_ERR(data->regmap);
+ }
+
+ ret = mt6360_check_vendor_info(data);
+ if (ret) {
+ dev_err(&client->dev, "Device not supported\n");
+ return ret;
+ }
+
+ ret = devm_regmap_add_irq_chip(&client->dev, data->regmap, client->irq,
+ IRQF_ONESHOT, 0, &mt6360_irq_chip,
+ &data->irq_data);
+ if (ret) {
+ dev_err(&client->dev, "Failed to add Regmap IRQ Chip\n");
+ return ret;
}
ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO,
mt6360_devs, ARRAY_SIZE(mt6360_devs), NULL,
- 0, regmap_irq_get_domain(mpd->irq_data));
+ 0, regmap_irq_get_domain(data->irq_data));
if (ret) {
dev_err(&client->dev,
"Failed to register subordinate devices\n");
return ret;
}
+ i2c_set_clientdata(client, data);
+
return 0;
}
-static int __maybe_unused mt6360_pmu_suspend(struct device *dev)
+static int __maybe_unused mt6360_suspend(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
@@ -390,7 +562,7 @@ static int __maybe_unused mt6360_pmu_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused mt6360_pmu_resume(struct device *dev)
+static int __maybe_unused mt6360_resume(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
@@ -401,25 +573,24 @@ static int __maybe_unused mt6360_pmu_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(mt6360_pmu_pm_ops,
- mt6360_pmu_suspend, mt6360_pmu_resume);
+static SIMPLE_DEV_PM_OPS(mt6360_pm_ops, mt6360_suspend, mt6360_resume);
-static const struct of_device_id __maybe_unused mt6360_pmu_of_id[] = {
- { .compatible = "mediatek,mt6360_pmu", },
+static const struct of_device_id __maybe_unused mt6360_of_id[] = {
+ { .compatible = "mediatek,mt6360", },
{},
};
-MODULE_DEVICE_TABLE(of, mt6360_pmu_of_id);
+MODULE_DEVICE_TABLE(of, mt6360_of_id);
-static struct i2c_driver mt6360_pmu_driver = {
+static struct i2c_driver mt6360_driver = {
.driver = {
- .name = "mt6360_pmu",
- .pm = &mt6360_pmu_pm_ops,
- .of_match_table = of_match_ptr(mt6360_pmu_of_id),
+ .name = "mt6360",
+ .pm = &mt6360_pm_ops,
+ .of_match_table = of_match_ptr(mt6360_of_id),
},
- .probe_new = mt6360_pmu_probe,
+ .probe_new = mt6360_probe,
};
-module_i2c_driver(mt6360_pmu_driver);
+module_i2c_driver(mt6360_driver);
MODULE_AUTHOR("Gene Chen <gene_chen@richtek.com>");
-MODULE_DESCRIPTION("MT6360 PMU I2C Driver");
+MODULE_DESCRIPTION("MT6360 I2C Driver");
MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/mt6360.h b/include/linux/mfd/mt6360.h
deleted file mode 100644
index ea13040..0000000
--- a/include/linux/mfd/mt6360.h
+++ /dev/null
@@ -1,240 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (c) 2020 MediaTek Inc.
- */
-
-#ifndef __MT6360_H__
-#define __MT6360_H__
-
-#include <linux/regmap.h>
-
-enum {
- MT6360_SLAVE_PMU = 0,
- MT6360_SLAVE_PMIC,
- MT6360_SLAVE_LDO,
- MT6360_SLAVE_TCPC,
- MT6360_SLAVE_MAX,
-};
-
-#define MT6360_PMU_SLAVEID (0x34)
-#define MT6360_PMIC_SLAVEID (0x1A)
-#define MT6360_LDO_SLAVEID (0x64)
-#define MT6360_TCPC_SLAVEID (0x4E)
-
-struct mt6360_pmu_data {
- struct i2c_client *i2c[MT6360_SLAVE_MAX];
- struct device *dev;
- struct regmap *regmap;
- struct regmap_irq_chip_data *irq_data;
- unsigned int chip_rev;
-};
-
-/* PMU register defininition */
-#define MT6360_PMU_DEV_INFO (0x00)
-#define MT6360_PMU_CORE_CTRL1 (0x01)
-#define MT6360_PMU_RST1 (0x02)
-#define MT6360_PMU_CRCEN (0x03)
-#define MT6360_PMU_RST_PAS_CODE1 (0x04)
-#define MT6360_PMU_RST_PAS_CODE2 (0x05)
-#define MT6360_PMU_CORE_CTRL2 (0x06)
-#define MT6360_PMU_TM_PAS_CODE1 (0x07)
-#define MT6360_PMU_TM_PAS_CODE2 (0x08)
-#define MT6360_PMU_TM_PAS_CODE3 (0x09)
-#define MT6360_PMU_TM_PAS_CODE4 (0x0A)
-#define MT6360_PMU_IRQ_IND (0x0B)
-#define MT6360_PMU_IRQ_MASK (0x0C)
-#define MT6360_PMU_IRQ_SET (0x0D)
-#define MT6360_PMU_SHDN_CTRL (0x0E)
-#define MT6360_PMU_TM_INF (0x0F)
-#define MT6360_PMU_I2C_CTRL (0x10)
-#define MT6360_PMU_CHG_CTRL1 (0x11)
-#define MT6360_PMU_CHG_CTRL2 (0x12)
-#define MT6360_PMU_CHG_CTRL3 (0x13)
-#define MT6360_PMU_CHG_CTRL4 (0x14)
-#define MT6360_PMU_CHG_CTRL5 (0x15)
-#define MT6360_PMU_CHG_CTRL6 (0x16)
-#define MT6360_PMU_CHG_CTRL7 (0x17)
-#define MT6360_PMU_CHG_CTRL8 (0x18)
-#define MT6360_PMU_CHG_CTRL9 (0x19)
-#define MT6360_PMU_CHG_CTRL10 (0x1A)
-#define MT6360_PMU_CHG_CTRL11 (0x1B)
-#define MT6360_PMU_CHG_CTRL12 (0x1C)
-#define MT6360_PMU_CHG_CTRL13 (0x1D)
-#define MT6360_PMU_CHG_CTRL14 (0x1E)
-#define MT6360_PMU_CHG_CTRL15 (0x1F)
-#define MT6360_PMU_CHG_CTRL16 (0x20)
-#define MT6360_PMU_CHG_AICC_RESULT (0x21)
-#define MT6360_PMU_DEVICE_TYPE (0x22)
-#define MT6360_PMU_QC_CONTROL1 (0x23)
-#define MT6360_PMU_QC_CONTROL2 (0x24)
-#define MT6360_PMU_QC30_CONTROL1 (0x25)
-#define MT6360_PMU_QC30_CONTROL2 (0x26)
-#define MT6360_PMU_USB_STATUS1 (0x27)
-#define MT6360_PMU_QC_STATUS1 (0x28)
-#define MT6360_PMU_QC_STATUS2 (0x29)
-#define MT6360_PMU_CHG_PUMP (0x2A)
-#define MT6360_PMU_CHG_CTRL17 (0x2B)
-#define MT6360_PMU_CHG_CTRL18 (0x2C)
-#define MT6360_PMU_CHRDET_CTRL1 (0x2D)
-#define MT6360_PMU_CHRDET_CTRL2 (0x2E)
-#define MT6360_PMU_DPDN_CTRL (0x2F)
-#define MT6360_PMU_CHG_HIDDEN_CTRL1 (0x30)
-#define MT6360_PMU_CHG_HIDDEN_CTRL2 (0x31)
-#define MT6360_PMU_CHG_HIDDEN_CTRL3 (0x32)
-#define MT6360_PMU_CHG_HIDDEN_CTRL4 (0x33)
-#define MT6360_PMU_CHG_HIDDEN_CTRL5 (0x34)
-#define MT6360_PMU_CHG_HIDDEN_CTRL6 (0x35)
-#define MT6360_PMU_CHG_HIDDEN_CTRL7 (0x36)
-#define MT6360_PMU_CHG_HIDDEN_CTRL8 (0x37)
-#define MT6360_PMU_CHG_HIDDEN_CTRL9 (0x38)
-#define MT6360_PMU_CHG_HIDDEN_CTRL10 (0x39)
-#define MT6360_PMU_CHG_HIDDEN_CTRL11 (0x3A)
-#define MT6360_PMU_CHG_HIDDEN_CTRL12 (0x3B)
-#define MT6360_PMU_CHG_HIDDEN_CTRL13 (0x3C)
-#define MT6360_PMU_CHG_HIDDEN_CTRL14 (0x3D)
-#define MT6360_PMU_CHG_HIDDEN_CTRL15 (0x3E)
-#define MT6360_PMU_CHG_HIDDEN_CTRL16 (0x3F)
-#define MT6360_PMU_CHG_HIDDEN_CTRL17 (0x40)
-#define MT6360_PMU_CHG_HIDDEN_CTRL18 (0x41)
-#define MT6360_PMU_CHG_HIDDEN_CTRL19 (0x42)
-#define MT6360_PMU_CHG_HIDDEN_CTRL20 (0x43)
-#define MT6360_PMU_CHG_HIDDEN_CTRL21 (0x44)
-#define MT6360_PMU_CHG_HIDDEN_CTRL22 (0x45)
-#define MT6360_PMU_CHG_HIDDEN_CTRL23 (0x46)
-#define MT6360_PMU_CHG_HIDDEN_CTRL24 (0x47)
-#define MT6360_PMU_CHG_HIDDEN_CTRL25 (0x48)
-#define MT6360_PMU_BC12_CTRL (0x49)
-#define MT6360_PMU_CHG_STAT (0x4A)
-#define MT6360_PMU_RESV1 (0x4B)
-#define MT6360_PMU_TYPEC_OTP_TH_SEL_CODEH (0x4E)
-#define MT6360_PMU_TYPEC_OTP_TH_SEL_CODEL (0x4F)
-#define MT6360_PMU_TYPEC_OTP_HYST_TH (0x50)
-#define MT6360_PMU_TYPEC_OTP_CTRL (0x51)
-#define MT6360_PMU_ADC_BAT_DATA_H (0x52)
-#define MT6360_PMU_ADC_BAT_DATA_L (0x53)
-#define MT6360_PMU_IMID_BACKBST_ON (0x54)
-#define MT6360_PMU_IMID_BACKBST_OFF (0x55)
-#define MT6360_PMU_ADC_CONFIG (0x56)
-#define MT6360_PMU_ADC_EN2 (0x57)
-#define MT6360_PMU_ADC_IDLE_T (0x58)
-#define MT6360_PMU_ADC_RPT_1 (0x5A)
-#define MT6360_PMU_ADC_RPT_2 (0x5B)
-#define MT6360_PMU_ADC_RPT_3 (0x5C)
-#define MT6360_PMU_ADC_RPT_ORG1 (0x5D)
-#define MT6360_PMU_ADC_RPT_ORG2 (0x5E)
-#define MT6360_PMU_BAT_OVP_TH_SEL_CODEH (0x5F)
-#define MT6360_PMU_BAT_OVP_TH_SEL_CODEL (0x60)
-#define MT6360_PMU_CHG_CTRL19 (0x61)
-#define MT6360_PMU_VDDASUPPLY (0x62)
-#define MT6360_PMU_BC12_MANUAL (0x63)
-#define MT6360_PMU_CHGDET_FUNC (0x64)
-#define MT6360_PMU_FOD_CTRL (0x65)
-#define MT6360_PMU_CHG_CTRL20 (0x66)
-#define MT6360_PMU_CHG_HIDDEN_CTRL26 (0x67)
-#define MT6360_PMU_CHG_HIDDEN_CTRL27 (0x68)
-#define MT6360_PMU_RESV2 (0x69)
-#define MT6360_PMU_USBID_CTRL1 (0x6D)
-#define MT6360_PMU_USBID_CTRL2 (0x6E)
-#define MT6360_PMU_USBID_CTRL3 (0x6F)
-#define MT6360_PMU_FLED_CFG (0x70)
-#define MT6360_PMU_RESV3 (0x71)
-#define MT6360_PMU_FLED1_CTRL (0x72)
-#define MT6360_PMU_FLED_STRB_CTRL (0x73)
-#define MT6360_PMU_FLED1_STRB_CTRL2 (0x74)
-#define MT6360_PMU_FLED1_TOR_CTRL (0x75)
-#define MT6360_PMU_FLED2_CTRL (0x76)
-#define MT6360_PMU_RESV4 (0x77)
-#define MT6360_PMU_FLED2_STRB_CTRL2 (0x78)
-#define MT6360_PMU_FLED2_TOR_CTRL (0x79)
-#define MT6360_PMU_FLED_VMIDTRK_CTRL1 (0x7A)
-#define MT6360_PMU_FLED_VMID_RTM (0x7B)
-#define MT6360_PMU_FLED_VMIDTRK_CTRL2 (0x7C)
-#define MT6360_PMU_FLED_PWSEL (0x7D)
-#define MT6360_PMU_FLED_EN (0x7E)
-#define MT6360_PMU_FLED_Hidden1 (0x7F)
-#define MT6360_PMU_RGB_EN (0x80)
-#define MT6360_PMU_RGB1_ISNK (0x81)
-#define MT6360_PMU_RGB2_ISNK (0x82)
-#define MT6360_PMU_RGB3_ISNK (0x83)
-#define MT6360_PMU_RGB_ML_ISNK (0x84)
-#define MT6360_PMU_RGB1_DIM (0x85)
-#define MT6360_PMU_RGB2_DIM (0x86)
-#define MT6360_PMU_RGB3_DIM (0x87)
-#define MT6360_PMU_RESV5 (0x88)
-#define MT6360_PMU_RGB12_Freq (0x89)
-#define MT6360_PMU_RGB34_Freq (0x8A)
-#define MT6360_PMU_RGB1_Tr (0x8B)
-#define MT6360_PMU_RGB1_Tf (0x8C)
-#define MT6360_PMU_RGB1_TON_TOFF (0x8D)
-#define MT6360_PMU_RGB2_Tr (0x8E)
-#define MT6360_PMU_RGB2_Tf (0x8F)
-#define MT6360_PMU_RGB2_TON_TOFF (0x90)
-#define MT6360_PMU_RGB3_Tr (0x91)
-#define MT6360_PMU_RGB3_Tf (0x92)
-#define MT6360_PMU_RGB3_TON_TOFF (0x93)
-#define MT6360_PMU_RGB_Hidden_CTRL1 (0x94)
-#define MT6360_PMU_RGB_Hidden_CTRL2 (0x95)
-#define MT6360_PMU_RESV6 (0x97)
-#define MT6360_PMU_SPARE1 (0x9A)
-#define MT6360_PMU_SPARE2 (0xA0)
-#define MT6360_PMU_SPARE3 (0xB0)
-#define MT6360_PMU_SPARE4 (0xC0)
-#define MT6360_PMU_CHG_IRQ1 (0xD0)
-#define MT6360_PMU_CHG_IRQ2 (0xD1)
-#define MT6360_PMU_CHG_IRQ3 (0xD2)
-#define MT6360_PMU_CHG_IRQ4 (0xD3)
-#define MT6360_PMU_CHG_IRQ5 (0xD4)
-#define MT6360_PMU_CHG_IRQ6 (0xD5)
-#define MT6360_PMU_QC_IRQ (0xD6)
-#define MT6360_PMU_FOD_IRQ (0xD7)
-#define MT6360_PMU_BASE_IRQ (0xD8)
-#define MT6360_PMU_FLED_IRQ1 (0xD9)
-#define MT6360_PMU_FLED_IRQ2 (0xDA)
-#define MT6360_PMU_RGB_IRQ (0xDB)
-#define MT6360_PMU_BUCK1_IRQ (0xDC)
-#define MT6360_PMU_BUCK2_IRQ (0xDD)
-#define MT6360_PMU_LDO_IRQ1 (0xDE)
-#define MT6360_PMU_LDO_IRQ2 (0xDF)
-#define MT6360_PMU_CHG_STAT1 (0xE0)
-#define MT6360_PMU_CHG_STAT2 (0xE1)
-#define MT6360_PMU_CHG_STAT3 (0xE2)
-#define MT6360_PMU_CHG_STAT4 (0xE3)
-#define MT6360_PMU_CHG_STAT5 (0xE4)
-#define MT6360_PMU_CHG_STAT6 (0xE5)
-#define MT6360_PMU_QC_STAT (0xE6)
-#define MT6360_PMU_FOD_STAT (0xE7)
-#define MT6360_PMU_BASE_STAT (0xE8)
-#define MT6360_PMU_FLED_STAT1 (0xE9)
-#define MT6360_PMU_FLED_STAT2 (0xEA)
-#define MT6360_PMU_RGB_STAT (0xEB)
-#define MT6360_PMU_BUCK1_STAT (0xEC)
-#define MT6360_PMU_BUCK2_STAT (0xED)
-#define MT6360_PMU_LDO_STAT1 (0xEE)
-#define MT6360_PMU_LDO_STAT2 (0xEF)
-#define MT6360_PMU_CHG_MASK1 (0xF0)
-#define MT6360_PMU_CHG_MASK2 (0xF1)
-#define MT6360_PMU_CHG_MASK3 (0xF2)
-#define MT6360_PMU_CHG_MASK4 (0xF3)
-#define MT6360_PMU_CHG_MASK5 (0xF4)
-#define MT6360_PMU_CHG_MASK6 (0xF5)
-#define MT6360_PMU_QC_MASK (0xF6)
-#define MT6360_PMU_FOD_MASK (0xF7)
-#define MT6360_PMU_BASE_MASK (0xF8)
-#define MT6360_PMU_FLED_MASK1 (0xF9)
-#define MT6360_PMU_FLED_MASK2 (0xFA)
-#define MT6360_PMU_FAULTB_MASK (0xFB)
-#define MT6360_PMU_BUCK1_MASK (0xFC)
-#define MT6360_PMU_BUCK2_MASK (0xFD)
-#define MT6360_PMU_LDO_MASK1 (0xFE)
-#define MT6360_PMU_LDO_MASK2 (0xFF)
-#define MT6360_PMU_MAXREG (MT6360_PMU_LDO_MASK2)
-
-/* MT6360_PMU_IRQ_SET */
-#define MT6360_PMU_IRQ_REGNUM (MT6360_PMU_LDO_IRQ2 - MT6360_PMU_CHG_IRQ1 + 1)
-#define MT6360_IRQ_RETRIG BIT(2)
-
-#define CHIP_VEN_MASK (0xF0)
-#define CHIP_VEN_MT6360 (0x50)
-#define CHIP_REV_MASK (0x0F)
-
-#endif /* __MT6360_H__ */
--
2.7.4
_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v2 3/4] iio: adc: mt6360: Add ADC driver for MT6360
2020-06-19 11:53 [PATCH v2 0/4] dt-bindings: mfd: Add bindings for the Mediatek MT6360 Gene Chen
2020-06-19 11:53 ` [PATCH v2 1/4] dt-bindings: mfd: Add bindings for the Mediatek MT6360 PMIC Gene Chen
2020-06-19 11:53 ` [PATCH v2 2/4] mfd: mt6360: implement i2c R/W with CRC Gene Chen
@ 2020-06-19 11:53 ` Gene Chen
2020-06-20 17:03 ` Jonathan Cameron
2020-06-19 11:53 ` [PATCH v2 4/4] regulator: mt6360: Add support for MT6360 regulator Gene Chen
2020-06-20 16:55 ` [PATCH v2 0/4] dt-bindings: mfd: Add bindings for the Mediatek MT6360 Jonathan Cameron
4 siblings, 1 reply; 10+ messages in thread
From: Gene Chen @ 2020-06-19 11:53 UTC (permalink / raw)
To: lee.jones, jic23, jacek.anaszewski, pavel, matthias.bgg
Cc: gene_chen, lars, lgirdwood, linux-kernel, cy_huang,
benjamin.chao, broonie, linux-mediatek, dmurphy, pmeerw,
knaack.h, linux-arm-kernel, shufan_lee
From: Gene Chen <gene_chen@richtek.com>
Add MT6360 ADC driver include Charger Current, Voltage, and
Temperature.
Signed-off-by: Gene Chen <gene_chen@richtek.com>
---
drivers/iio/adc/Kconfig | 11 ++
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/mt6360-adc.c | 388 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 400 insertions(+)
create mode 100644 drivers/iio/adc/mt6360-adc.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index ff35696..7c77424 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -702,6 +702,17 @@ config MCP3911
This driver can also be built as a module. If so, the module will be
called mcp3911.
+config MEDIATEK_MT6360_ADC
+ tristate "Mediatek MT6360 ADC Part"
+ depends on MFD_MT6360
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say Y here to enable MT6360 ADC Part.
+ Integrated for System Monitoring include
+ Charger and Battery Current, Voltage and
+ Temperature
+
config MEDIATEK_MT6577_AUXADC
tristate "MediaTek AUXADC driver"
depends on ARCH_MEDIATEK || COMPILE_TEST
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 90f94ad..5fca90a 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_MAX9611) += max9611.o
obj-$(CONFIG_MCP320X) += mcp320x.o
obj-$(CONFIG_MCP3422) += mcp3422.o
obj-$(CONFIG_MCP3911) += mcp3911.o
+obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
diff --git a/drivers/iio/adc/mt6360-adc.c b/drivers/iio/adc/mt6360-adc.c
new file mode 100644
index 0000000..a8ca80d
--- /dev/null
+++ b/drivers/iio/adc/mt6360-adc.c
@@ -0,0 +1,388 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 MediaTek Inc.
+ *
+ * Author: Gene Chen <gene_chen@richtek.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/ktime.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define MT6360_REG_PMUCHGCTRL3 0x313
+#define MT6360_REG_PMUADCCFG 0x356
+#define MT6360_REG_PMUADCRPT1 0x35A
+
+/* PMUCHGCTRL3 0x313 */
+#define MT6360_AICR_MASK 0xFC
+#define MT6360_AICR_SHFT 2
+#define MT6360_AICR_400MA 0x6
+/* PMUADCCFG 0x356 */
+#define MT6360_ADCEN_MASK 0x8000
+/* PMUADCRPT1 0x35A */
+#define MT6360_PREFERCH_MASK 0xF0
+#define MT6360_PREFERCH_SHFT 4
+#define MT6360_RPTCH_MASK 0x0F
+
+enum {
+ MT6360_CHAN_USBID = 0,
+ MT6360_CHAN_VBUSDIV5,
+ MT6360_CHAN_VBUSDIV2,
+ MT6360_CHAN_VSYS,
+ MT6360_CHAN_VBAT,
+ MT6360_CHAN_IBUS,
+ MT6360_CHAN_IBAT,
+ MT6360_CHAN_CHG_VDDP,
+ MT6360_CHAN_TEMP_JC,
+ MT6360_CHAN_VREF_TS,
+ MT6360_CHAN_TS,
+ MT6360_CHAN_MAX,
+};
+
+struct mt6360_adc_data {
+ struct device *dev;
+ struct regmap *regmap;
+ struct completion adc_complete;
+ struct mutex adc_lock;
+ ktime_t last_off_timestamps[MT6360_CHAN_MAX];
+ int irq;
+};
+
+static inline int mt6360_adc_val_converter(int val, int multiplier,
+ int offset, int divisor)
+{
+ return ((val * multiplier) + offset) / divisor;
+}
+
+static int mt6360_adc_convert_processed_val(struct mt6360_adc_data *info,
+ int chan_idx, int *val)
+{
+ unsigned int regval = 0;
+ const struct converter {
+ int multiplier;
+ int offset;
+ int divisor;
+ } adc_converter[MT6360_CHAN_MAX] = {
+ { 1250, 0, 1}, /* USBID */
+ { 6250, 0, 1}, /* VBUSDIV5 */
+ { 2500, 0, 1}, /* VBUSDIV2 */
+ { 1250, 0, 1}, /* VSYS */
+ { 1250, 0, 1}, /* VBAT */
+ { 2500, 0, 1}, /* IBUS */
+ { 2500, 0, 1}, /* IBAT */
+ { 1250, 0, 1}, /* CHG_VDDP */
+ { 105, -8000, 100}, /* TEMP_JC */
+ { 1250, 0, 1}, /* VREF_TS */
+ { 1250, 0, 1}, /* TS */
+ }, sp_ibus_adc_converter = { 1900, 0, 1 }, *sel_converter;
+ int ret;
+
+ sel_converter = adc_converter + chan_idx;
+ if (chan_idx == MT6360_CHAN_IBUS) {
+ /* ibus chan will be affected by aicr config */
+ /* if aicr < 400, apply the special ibus converter */
+ ret = regmap_read(info->regmap,
+ MT6360_REG_PMUCHGCTRL3, ®val);
+ if (ret)
+ return ret;
+
+ regval = (regval & MT6360_AICR_MASK) >> MT6360_AICR_SHFT;
+ if (regval < MT6360_AICR_400MA)
+ sel_converter = &sp_ibus_adc_converter;
+ }
+
+ *val = mt6360_adc_val_converter(*val, sel_converter->multiplier,
+ sel_converter->offset,
+ sel_converter->divisor);
+
+ return 0;
+}
+
+static int mt6360_adc_read_processed(struct mt6360_adc_data *mad,
+ int channel, int *val)
+{
+ u16 adc_enable;
+ u8 rpt[3];
+ ktime_t start_t, predict_end_t;
+ long timeout;
+ int value, ret;
+
+ mutex_lock(&mad->adc_lock);
+
+ /* select preferred channel that we want */
+ ret = regmap_update_bits(mad->regmap,
+ MT6360_REG_PMUADCRPT1, MT6360_PREFERCH_MASK,
+ channel << MT6360_PREFERCH_SHFT);
+ if (ret)
+ goto out_adc;
+
+ /* enable adc channel we want and adc_en */
+ adc_enable = MT6360_ADCEN_MASK | BIT(channel);
+ adc_enable = cpu_to_be16(adc_enable);
+ ret = regmap_raw_write(mad->regmap, MT6360_REG_PMUADCCFG,
+ (void *)&adc_enable, sizeof(u16));
+ if (ret)
+ goto out_adc;
+
+ start_t = ktime_get();
+ predict_end_t = ktime_add_ms(mad->last_off_timestamps[channel], 50);
+
+ if (ktime_after(start_t, predict_end_t))
+ predict_end_t = ktime_add_ms(start_t, 25);
+ else
+ predict_end_t = ktime_add_ms(start_t, 75);
+
+ enable_irq(mad->irq);
+adc_retry:
+ reinit_completion(&mad->adc_complete);
+
+ /* wait for conversion to complete */
+ timeout = wait_for_completion_timeout(&mad->adc_complete,
+ msecs_to_jiffies(200));
+ if (timeout == 0) {
+ ret = -ETIMEDOUT;
+ goto out_adc_conv;
+ } else if (timeout < 0) {
+ ret = -EINTR;
+ goto out_adc_conv;
+ }
+
+ ret = regmap_raw_read(mad->regmap,
+ MT6360_REG_PMUADCRPT1, rpt, sizeof(rpt));
+ if (ret)
+ goto out_adc_conv;
+
+ /* check the current reported channel */
+ if ((rpt[0] & MT6360_RPTCH_MASK) != channel) {
+ dev_dbg(mad->dev,
+ "not wanted channel report [%02x]\n", rpt[0]);
+ goto adc_retry;
+ }
+
+ if (!ktime_after(ktime_get(), predict_end_t)) {
+ dev_dbg(mad->dev, "time is not after one adc_conv_t\n");
+ goto adc_retry;
+ }
+
+ value = (rpt[1] << 8) | rpt[2];
+
+ ret = mt6360_adc_convert_processed_val(mad, channel, &value);
+ if (ret)
+ goto out_adc_conv;
+
+ *val = value;
+ ret = IIO_VAL_INT;
+
+out_adc_conv:
+ disable_irq(mad->irq);
+ adc_enable = MT6360_ADCEN_MASK;
+ adc_enable = cpu_to_be16(adc_enable);
+ regmap_raw_write(mad->regmap, MT6360_REG_PMUADCCFG,
+ (void *)&adc_enable, sizeof(u16));
+ mad->last_off_timestamps[channel] = ktime_get();
+ /* set prefer channel to 0xf */
+ regmap_update_bits(mad->regmap, MT6360_REG_PMUADCRPT1,
+ MT6360_PREFERCH_MASK, 0xF << MT6360_PREFERCH_SHFT);
+out_adc:
+ mutex_unlock(&mad->adc_lock);
+
+ return ret;
+}
+
+static int mt6360_adc_read_raw(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct mt6360_adc_data *mad = iio_priv(iio_dev);
+
+ if (mask == IIO_CHAN_INFO_PROCESSED)
+ return mt6360_adc_read_processed(mad, chan->channel, val);
+
+ return -EINVAL;
+}
+
+static const struct iio_info mt6360_adc_iio_info = {
+ .read_raw = mt6360_adc_read_raw,
+};
+
+#define MT6360_ADC_CHAN(_idx, _type) { \
+ .type = _type, \
+ .channel = MT6360_CHAN_##_idx, \
+ .scan_index = MT6360_CHAN_##_idx, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 32, \
+ .storagebits = 32, \
+ .shift = 0, \
+ .endianness = IIO_CPU, \
+ }, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+ .extend_name = #_idx, \
+ .datasheet_name = #_idx, \
+ .indexed = 1, \
+}
+
+static const struct iio_chan_spec mt6360_adc_channels[] = {
+ MT6360_ADC_CHAN(USBID, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(VBUSDIV5, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(VBUSDIV2, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(VSYS, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(VBAT, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(IBUS, IIO_CURRENT),
+ MT6360_ADC_CHAN(IBAT, IIO_CURRENT),
+ MT6360_ADC_CHAN(CHG_VDDP, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(TEMP_JC, IIO_TEMP),
+ MT6360_ADC_CHAN(VREF_TS, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(TS, IIO_VOLTAGE),
+ IIO_CHAN_SOFT_TIMESTAMP(MT6360_CHAN_MAX),
+};
+
+static irqreturn_t mt6360_pmu_adc_donei_handler(int irq, void *data)
+{
+ struct mt6360_adc_data *mad = iio_priv(data);
+
+ complete(&mad->adc_complete);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mt6360_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ /* 11 ch s32 numbers + 1 s64 timestamp */
+ s32 data[MT6360_CHAN_MAX + 2] = { };
+ int i = 0, bit, val, ret;
+
+ for_each_set_bit(bit,
+ indio_dev->active_scan_mask, indio_dev->masklength) {
+ const struct iio_chan_spec *chan = indio_dev->channels + bit;
+
+ ret = mt6360_adc_read_raw(indio_dev, chan, &val,
+ NULL, IIO_CHAN_INFO_PROCESSED);
+ if (ret != IIO_VAL_INT) {
+ dev_warn(&indio_dev->dev,
+ "Failed to get %d conversion val\n", bit);
+ goto out;
+ }
+
+ data[i++] = val;
+ }
+ iio_push_to_buffers_with_timestamp(indio_dev,
+ data, iio_get_time_ns(indio_dev));
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static inline int mt6360_adc_reset(struct mt6360_adc_data *info)
+{
+ u8 configs[3] = {0x80, 0, 0};
+ ktime_t all_off_time;
+ int i;
+
+ all_off_time = ktime_get();
+ for (i = 0; i < MT6360_CHAN_MAX; i++)
+ info->last_off_timestamps[i] = all_off_time;
+
+ /* enable adc_en, clear adc_chn_en/zcv_en/adc_wait_t/adc_idle_t */
+ return regmap_raw_write(info->regmap,
+ MT6360_REG_PMUADCCFG, configs, sizeof(configs));
+}
+
+static int mt6360_adc_probe(struct platform_device *pdev)
+{
+ struct mt6360_adc_data *mad;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*mad));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ mad = iio_priv(indio_dev);
+ mad->dev = &pdev->dev;
+ init_completion(&mad->adc_complete);
+ mutex_init(&mad->adc_lock);
+
+ mad->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!mad->regmap) {
+ dev_err(&pdev->dev, "Failed to get parent regmap\n");
+ return -ENODEV;
+ }
+
+ ret = mt6360_adc_reset(mad);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to reset adc\n");
+ return ret;
+ }
+
+ mad->irq = platform_get_irq_byname(pdev, "adc_donei");
+ if (mad->irq < 0) {
+ dev_err(&pdev->dev, "Failed to get adc_done irq\n");
+ return mad->irq;
+ }
+
+ irq_set_status_flags(mad->irq, IRQ_NOAUTOEN);
+ ret = devm_request_threaded_irq(&pdev->dev, mad->irq, NULL,
+ mt6360_pmu_adc_donei_handler,
+ IRQF_TRIGGER_NONE, "adc_donei",
+ indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register adc_done irq\n");
+ return ret;
+ }
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &mt6360_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = mt6360_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mt6360_adc_channels);
+
+ ret = devm_iio_triggered_buffer_setup(&pdev->dev, indio_dev, NULL,
+ mt6360_adc_trigger_handler, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to allocate iio trigger buffer\n");
+ return ret;
+ }
+
+ ret = devm_iio_device_register(&pdev->dev, indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register iio device\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ return 0;
+}
+
+static const struct of_device_id __maybe_unused mt6360_adc_of_id[] = {
+ { .compatible = "mediatek,mt6360-adc", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt6360_adc_of_id);
+
+static struct platform_driver mt6360_adc_driver = {
+ .driver = {
+ .name = "mt6360-adc",
+ .of_match_table = mt6360_adc_of_id,
+ },
+ .probe = mt6360_adc_probe,
+};
+module_platform_driver(mt6360_adc_driver);
+
+MODULE_AUTHOR("Gene Chen <gene_chen@richtek.com>");
+MODULE_DESCRIPTION("MT6360 ADC Driver");
+MODULE_LICENSE("GPL v2");
--
2.7.4
_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v2 4/4] regulator: mt6360: Add support for MT6360 regulator
2020-06-19 11:53 [PATCH v2 0/4] dt-bindings: mfd: Add bindings for the Mediatek MT6360 Gene Chen
` (2 preceding siblings ...)
2020-06-19 11:53 ` [PATCH v2 3/4] iio: adc: mt6360: Add ADC driver for MT6360 Gene Chen
@ 2020-06-19 11:53 ` Gene Chen
2020-06-20 16:55 ` [PATCH v2 0/4] dt-bindings: mfd: Add bindings for the Mediatek MT6360 Jonathan Cameron
4 siblings, 0 replies; 10+ messages in thread
From: Gene Chen @ 2020-06-19 11:53 UTC (permalink / raw)
To: lee.jones, jic23, jacek.anaszewski, pavel, matthias.bgg
Cc: gene_chen, lars, lgirdwood, linux-kernel, cy_huang,
benjamin.chao, broonie, linux-mediatek, dmurphy, pmeerw,
knaack.h, linux-arm-kernel, shufan_lee
From: Gene Chen <gene_chen@richtek.com>
Add MT6360 regulator driver include 2-channel buck and
6-channel ldo
Signed-off-by: Gene Chen <gene_chen@richtek.com>
---
drivers/regulator/Kconfig | 9 +
drivers/regulator/Makefile | 1 +
drivers/regulator/mt6360-regulator.c | 485 +++++++++++++++++++++++++++++++++++
3 files changed, 495 insertions(+)
create mode 100644 drivers/regulator/mt6360-regulator.c
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 8f677f5..9ae5711 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -691,6 +691,15 @@ config REGULATOR_MT6358
This driver supports the control of different power rails of device
through regulator interface.
+config REGULATOR_MT6360
+ tristate "MT6360 SubPMIC Regulator"
+ depends on MFD_MT6360
+ help
+ Say Y here to enable MT6360 regulator support.
+ This is support MT6360 PMIC/LDO part include
+ 2-channel buck with Thermal Shutdown and Overload Protection
+ 6-channel High PSRR and Low Dropout LDO.
+
config REGULATOR_MT6380
tristate "MediaTek MT6380 PMIC"
depends on MTK_PMIC_WRAP
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index e8f1633..7256457 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_REGULATOR_MPQ7920) += mpq7920.o
obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o
obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o
obj-$(CONFIG_REGULATOR_MT6358) += mt6358-regulator.o
+obj-$(CONFIG_REGULATOR_MT6360) += mt6360-regulator.o
obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o
obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
diff --git a/drivers/regulator/mt6360-regulator.c b/drivers/regulator/mt6360-regulator.c
new file mode 100644
index 0000000..f3c8911
--- /dev/null
+++ b/drivers/regulator/mt6360-regulator.c
@@ -0,0 +1,485 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (c) 2020 MediaTek Inc.
+
+// Author: Gene Chen <gene_chen@richtek.com>
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/version.h>
+
+#include <dt-bindings/mfd/mt6360.h>
+
+enum {
+ MT6360_REGULATOR_BUCK1 = 0,
+ MT6360_REGULATOR_BUCK2,
+ MT6360_REGULATOR_LDO6,
+ MT6360_REGULATOR_LDO7,
+ MT6360_REGULATOR_LDO1,
+ MT6360_REGULATOR_LDO2,
+ MT6360_REGULATOR_LDO3,
+ MT6360_REGULATOR_LDO5,
+ MT6360_REGULATOR_MAX,
+};
+
+struct mt6360_irq_mapping {
+ const char *name;
+ irq_handler_t handler;
+};
+
+struct mt6360_regulator_desc {
+ const struct regulator_desc desc;
+ unsigned int mode_reg;
+ unsigned int mode_mask;
+ unsigned int state_reg;
+ unsigned int state_mask;
+ const struct mt6360_irq_mapping *irq_tables;
+ int irq_table_size;
+};
+
+struct mt6360_regulator_data {
+ struct device *dev;
+ struct regmap *regmap;
+};
+
+static irqreturn_t mt6360_pgb_event_handler(int irq, void *data)
+{
+ struct regulator_dev *rdev = data;
+
+ regulator_notifier_call_chain(rdev, REGULATOR_EVENT_FAIL, NULL);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mt6360_oc_event_handler(int irq, void *data)
+{
+ struct regulator_dev *rdev = data;
+
+ regulator_notifier_call_chain(rdev, REGULATOR_EVENT_OVER_CURRENT, NULL);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mt6360_ov_event_handler(int irq, void *data)
+{
+ struct regulator_dev *rdev = data;
+
+ regulator_notifier_call_chain(rdev,
+ REGULATOR_EVENT_REGULATION_OUT, NULL);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mt6360_uv_event_handler(int irq, void *data)
+{
+ struct regulator_dev *rdev = data;
+
+ regulator_notifier_call_chain(rdev,
+ REGULATOR_EVENT_UNDER_VOLTAGE, NULL);
+ return IRQ_HANDLED;
+}
+
+static const struct mt6360_irq_mapping buck1_irq_tbls[] = {
+ { "buck1_pgb_evt", mt6360_pgb_event_handler },
+ { "buck1_oc_evt", mt6360_oc_event_handler },
+ { "buck1_ov_evt", mt6360_ov_event_handler },
+ { "buck1_uv_evt", mt6360_uv_event_handler },
+};
+
+static const struct mt6360_irq_mapping buck2_irq_tbls[] = {
+ { "buck2_pgb_evt", mt6360_pgb_event_handler },
+ { "buck2_oc_evt", mt6360_oc_event_handler },
+ { "buck2_ov_evt", mt6360_ov_event_handler },
+ { "buck2_uv_evt", mt6360_uv_event_handler },
+};
+
+static const struct mt6360_irq_mapping ldo6_irq_tbls[] = {
+ { "ldo6_pgb_evt", mt6360_pgb_event_handler },
+ { "ldo6_oc_evt", mt6360_oc_event_handler },
+};
+
+static const struct mt6360_irq_mapping ldo7_irq_tbls[] = {
+ { "ldo7_pgb_evt", mt6360_pgb_event_handler },
+ { "ldo7_oc_evt", mt6360_oc_event_handler },
+};
+
+static const struct mt6360_irq_mapping ldo1_irq_tbls[] = {
+ { "ldo1_pgb_evt", mt6360_pgb_event_handler },
+ { "ldo1_oc_evt", mt6360_oc_event_handler },
+};
+
+static const struct mt6360_irq_mapping ldo2_irq_tbls[] = {
+ { "ldo2_pgb_evt", mt6360_pgb_event_handler },
+ { "ldo2_oc_evt", mt6360_oc_event_handler },
+};
+
+static const struct mt6360_irq_mapping ldo3_irq_tbls[] = {
+ { "ldo3_pgb_evt", mt6360_pgb_event_handler },
+ { "ldo3_oc_evt", mt6360_oc_event_handler },
+};
+
+static const struct mt6360_irq_mapping ldo5_irq_tbls[] = {
+ { "ldo5_pgb_evt", mt6360_pgb_event_handler },
+ { "ldo5_oc_evt", mt6360_oc_event_handler },
+};
+
+static const struct linear_range buck_vout_ranges[] = {
+ REGULATOR_LINEAR_RANGE(300000, 0x00, 0xc7, 5000),
+ REGULATOR_LINEAR_RANGE(1300000, 0xc8, 0xff, 0),
+};
+
+static const struct linear_range ldo_vout_ranges1[] = {
+ REGULATOR_LINEAR_RANGE(500000, 0x00, 0x09, 10000),
+ REGULATOR_LINEAR_RANGE(600000, 0x0a, 0x10, 0),
+ REGULATOR_LINEAR_RANGE(610000, 0x11, 0x19, 10000),
+ REGULATOR_LINEAR_RANGE(700000, 0x1a, 0x20, 0),
+ REGULATOR_LINEAR_RANGE(710000, 0x21, 0x29, 10000),
+ REGULATOR_LINEAR_RANGE(800000, 0x2a, 0x30, 0),
+ REGULATOR_LINEAR_RANGE(810000, 0x31, 0x39, 10000),
+ REGULATOR_LINEAR_RANGE(900000, 0x3a, 0x40, 0),
+ REGULATOR_LINEAR_RANGE(910000, 0x41, 0x49, 10000),
+ REGULATOR_LINEAR_RANGE(1000000, 0x4a, 0x50, 0),
+ REGULATOR_LINEAR_RANGE(1010000, 0x51, 0x59, 10000),
+ REGULATOR_LINEAR_RANGE(1100000, 0x5a, 0x60, 0),
+ REGULATOR_LINEAR_RANGE(1110000, 0x61, 0x69, 10000),
+ REGULATOR_LINEAR_RANGE(1200000, 0x6a, 0x70, 0),
+ REGULATOR_LINEAR_RANGE(1210000, 0x71, 0x79, 10000),
+ REGULATOR_LINEAR_RANGE(1300000, 0x7a, 0x80, 0),
+ REGULATOR_LINEAR_RANGE(1310000, 0x81, 0x89, 10000),
+ REGULATOR_LINEAR_RANGE(1400000, 0x8a, 0x90, 0),
+ REGULATOR_LINEAR_RANGE(1410000, 0x91, 0x99, 10000),
+ REGULATOR_LINEAR_RANGE(1500000, 0x9a, 0xa0, 0),
+ REGULATOR_LINEAR_RANGE(1510000, 0xa1, 0xa9, 10000),
+ REGULATOR_LINEAR_RANGE(1600000, 0xaa, 0xb0, 0),
+ REGULATOR_LINEAR_RANGE(1610000, 0xb1, 0xb9, 10000),
+ REGULATOR_LINEAR_RANGE(1700000, 0xba, 0xc0, 0),
+ REGULATOR_LINEAR_RANGE(1710000, 0xc1, 0xc9, 10000),
+ REGULATOR_LINEAR_RANGE(1800000, 0xca, 0xd0, 0),
+ REGULATOR_LINEAR_RANGE(1810000, 0xd1, 0xd9, 10000),
+ REGULATOR_LINEAR_RANGE(1900000, 0xda, 0xe0, 0),
+ REGULATOR_LINEAR_RANGE(1910000, 0xe1, 0xe9, 10000),
+ REGULATOR_LINEAR_RANGE(2000000, 0xea, 0xf0, 0),
+ REGULATOR_LINEAR_RANGE(2010000, 0xf1, 0xf9, 10000),
+ REGULATOR_LINEAR_RANGE(2100000, 0xfa, 0xff, 0),
+};
+
+static const struct linear_range ldo_vout_ranges2[] = {
+ REGULATOR_LINEAR_RANGE(1200000, 0x00, 0x09, 10000),
+ REGULATOR_LINEAR_RANGE(1300000, 0x0a, 0x10, 0),
+ REGULATOR_LINEAR_RANGE(1310000, 0x11, 0x19, 10000),
+ REGULATOR_LINEAR_RANGE(1400000, 0x1a, 0x1f, 0),
+ REGULATOR_LINEAR_RANGE(1500000, 0x20, 0x29, 10000),
+ REGULATOR_LINEAR_RANGE(1600000, 0x2a, 0x2f, 0),
+ REGULATOR_LINEAR_RANGE(1700000, 0x30, 0x39, 10000),
+ REGULATOR_LINEAR_RANGE(1800000, 0x3a, 0x40, 0),
+ REGULATOR_LINEAR_RANGE(1810000, 0x41, 0x49, 10000),
+ REGULATOR_LINEAR_RANGE(1900000, 0x4a, 0x4f, 0),
+ REGULATOR_LINEAR_RANGE(2000000, 0x50, 0x59, 10000),
+ REGULATOR_LINEAR_RANGE(2100000, 0x5a, 0x60, 0),
+ REGULATOR_LINEAR_RANGE(2110000, 0x61, 0x69, 10000),
+ REGULATOR_LINEAR_RANGE(2200000, 0x6a, 0x6f, 0),
+ REGULATOR_LINEAR_RANGE(2500000, 0x70, 0x79, 10000),
+ REGULATOR_LINEAR_RANGE(2600000, 0x7a, 0x7f, 0),
+ REGULATOR_LINEAR_RANGE(2700000, 0x80, 0x89, 10000),
+ REGULATOR_LINEAR_RANGE(2800000, 0x8a, 0x90, 0),
+ REGULATOR_LINEAR_RANGE(2810000, 0x91, 0x99, 10000),
+ REGULATOR_LINEAR_RANGE(2900000, 0x9a, 0xa0, 0),
+ REGULATOR_LINEAR_RANGE(2910000, 0xa1, 0xa9, 10000),
+ REGULATOR_LINEAR_RANGE(3000000, 0xaa, 0xb0, 0),
+ REGULATOR_LINEAR_RANGE(3010000, 0xb1, 0xb9, 10000),
+ REGULATOR_LINEAR_RANGE(3100000, 0xba, 0xc0, 0),
+ REGULATOR_LINEAR_RANGE(3110000, 0xc1, 0xc9, 10000),
+ REGULATOR_LINEAR_RANGE(3200000, 0xca, 0xcf, 0),
+ REGULATOR_LINEAR_RANGE(3300000, 0xd0, 0xd9, 10000),
+ REGULATOR_LINEAR_RANGE(3400000, 0xda, 0xe0, 0),
+ REGULATOR_LINEAR_RANGE(3410000, 0xe1, 0xe9, 10000),
+ REGULATOR_LINEAR_RANGE(3500000, 0xea, 0xf0, 0),
+ REGULATOR_LINEAR_RANGE(3510000, 0xf1, 0xf9, 10000),
+ REGULATOR_LINEAR_RANGE(3600000, 0xfa, 0xff, 0),
+};
+
+static const struct linear_range ldo_vout_ranges3[] = {
+ REGULATOR_LINEAR_RANGE(2700000, 0x00, 0x09, 10000),
+ REGULATOR_LINEAR_RANGE(2800000, 0x0a, 0x10, 0),
+ REGULATOR_LINEAR_RANGE(2810000, 0x11, 0x19, 10000),
+ REGULATOR_LINEAR_RANGE(2900000, 0x1a, 0x20, 0),
+ REGULATOR_LINEAR_RANGE(2910000, 0x21, 0x29, 10000),
+ REGULATOR_LINEAR_RANGE(3000000, 0x2a, 0x30, 0),
+ REGULATOR_LINEAR_RANGE(3010000, 0x31, 0x39, 10000),
+ REGULATOR_LINEAR_RANGE(3100000, 0x3a, 0x40, 0),
+ REGULATOR_LINEAR_RANGE(3110000, 0x41, 0x49, 10000),
+ REGULATOR_LINEAR_RANGE(3200000, 0x4a, 0x4f, 0),
+ REGULATOR_LINEAR_RANGE(3300000, 0x50, 0x59, 10000),
+ REGULATOR_LINEAR_RANGE(3400000, 0x5a, 0x60, 0),
+ REGULATOR_LINEAR_RANGE(3410000, 0x61, 0x69, 10000),
+ REGULATOR_LINEAR_RANGE(3500000, 0x6a, 0x70, 0),
+ REGULATOR_LINEAR_RANGE(3510000, 0x71, 0x79, 10000),
+ REGULATOR_LINEAR_RANGE(3600000, 0x7a, 0x7f, 0),
+};
+
+static int mt6360_regulator_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ const struct mt6360_regulator_desc *rdesc =
+ (const struct mt6360_regulator_desc *)rdev->desc;
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ int shift = ffs(rdesc->mode_mask) - 1;
+ unsigned int val;
+ int ret;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ val = MT6360_OPMODE_NORMAL;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = MT6360_OPMODE_ULP;
+ break;
+ case REGULATOR_MODE_IDLE:
+ val = MT6360_OPMODE_LP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(regmap, rdesc->mode_reg,
+ rdesc->mode_mask, val << shift);
+ if (ret) {
+ dev_err(&rdev->dev, "%s: fail (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static unsigned int mt6360_regulator_get_mode(struct regulator_dev *rdev)
+{
+ const struct mt6360_regulator_desc *rdesc =
+ (const struct mt6360_regulator_desc *)rdev->desc;
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ int shift = ffs(rdesc->mode_mask) - 1;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(regmap, rdesc->mode_reg, &val);
+ if (ret)
+ return ret;
+
+ val &= rdesc->mode_mask;
+ val >>= shift;
+
+ switch (val) {
+ case MT6360_OPMODE_LP:
+ return REGULATOR_MODE_IDLE;
+ case MT6360_OPMODE_ULP:
+ return REGULATOR_MODE_STANDBY;
+ case MT6360_OPMODE_NORMAL:
+ return REGULATOR_MODE_NORMAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mt6360_regulator_get_status(struct regulator_dev *rdev)
+{
+ const struct mt6360_regulator_desc *rdesc =
+ (const struct mt6360_regulator_desc *)rdev->desc;
+ struct regmap *regmap = rdev_get_regmap(rdev);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(regmap, rdesc->state_reg, &val);
+ if (ret)
+ return ret;
+
+ if (val & rdesc->state_mask)
+ return REGULATOR_STATUS_ON;
+
+ return REGULATOR_STATUS_OFF;
+}
+
+static const struct regulator_ops mt6360_regulator_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_mode = mt6360_regulator_set_mode,
+ .get_mode = mt6360_regulator_get_mode,
+ .get_status = mt6360_regulator_get_status,
+};
+
+static unsigned int mt6360_regulator_of_map_mode(unsigned int hw_mode)
+{
+ switch (hw_mode) {
+ case MT6360_OPMODE_NORMAL:
+ return REGULATOR_MODE_NORMAL;
+ case MT6360_OPMODE_LP:
+ return REGULATOR_MODE_IDLE;
+ case MT6360_OPMODE_ULP:
+ return REGULATOR_MODE_STANDBY;
+ default:
+ return REGULATOR_MODE_INVALID;
+ }
+}
+
+#define MT6360_REGULATOR_DESC(_name, _sname, ereg, emask, vreg, vmask, \
+ mreg, mmask, streg, stmask, vranges, \
+ vcnts, offon_delay, irq_tbls) \
+{ \
+ .desc = { \
+ .name = #_name, \
+ .supply_name = #_sname, \
+ .id = MT6360_REGULATOR_##_name, \
+ .of_match = of_match_ptr(#_name), \
+ .of_map_mode = mt6360_regulator_of_map_mode, \
+ .owner = THIS_MODULE, \
+ .ops = &mt6360_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .vsel_reg = vreg, \
+ .vsel_mask = vmask, \
+ .enable_reg = ereg, \
+ .enable_mask = emask, \
+ .linear_ranges = vranges, \
+ .n_linear_ranges = ARRAY_SIZE(vranges), \
+ .n_voltages = vcnts, \
+ .off_on_delay = offon_delay, \
+ }, \
+ .mode_reg = mreg, \
+ .mode_mask = mmask, \
+ .state_reg = streg, \
+ .state_mask = stmask, \
+ .irq_tables = irq_tbls, \
+ .irq_table_size = ARRAY_SIZE(irq_tbls), \
+}
+
+static const struct mt6360_regulator_desc mt6360_regulator_descs[] = {
+ MT6360_REGULATOR_DESC(BUCK1, BUCK1_VIN, 0x117, 0x40, 0x110, 0xff, 0x117,
+ 0x30, 0x117, 0x04, buck_vout_ranges, 256, 0,
+ buck1_irq_tbls),
+ MT6360_REGULATOR_DESC(BUCK2, BUCK2_VIN, 0x127, 0x40, 0x120, 0xff, 0x127,
+ 0x30, 0x127, 0x04, buck_vout_ranges, 256, 0,
+ buck2_irq_tbls),
+ MT6360_REGULATOR_DESC(LDO6, LDO_VIN3, 0x137, 0x40, 0x13B, 0xff, 0x137,
+ 0x30, 0x137, 0x04, ldo_vout_ranges1, 256, 0,
+ ldo6_irq_tbls),
+ MT6360_REGULATOR_DESC(LDO7, LDO_VIN3, 0x131, 0x40, 0x135, 0xff, 0x131,
+ 0x30, 0x131, 0x04, ldo_vout_ranges1, 256, 0,
+ ldo7_irq_tbls),
+ MT6360_REGULATOR_DESC(LDO1, LDO_VIN1, 0x217, 0x40, 0x21B, 0xff, 0x217,
+ 0x30, 0x217, 0x04, ldo_vout_ranges2, 256, 0,
+ ldo1_irq_tbls),
+ MT6360_REGULATOR_DESC(LDO2, LDO_VIN1, 0x211, 0x40, 0x215, 0xff, 0x211,
+ 0x30, 0x211, 0x04, ldo_vout_ranges2, 256, 0,
+ ldo2_irq_tbls),
+ MT6360_REGULATOR_DESC(LDO3, LDO_VIN1, 0x205, 0x40, 0x209, 0xff, 0x205,
+ 0x30, 0x205, 0x04, ldo_vout_ranges2, 256, 100,
+ ldo3_irq_tbls),
+ MT6360_REGULATOR_DESC(LDO5, LDO_VIN2, 0x20B, 0x40, 0x20F, 0x7f, 0x20B,
+ 0x30, 0x20B, 0x04, ldo_vout_ranges3, 128, 100,
+ ldo5_irq_tbls),
+};
+
+static int mt6360_regulator_irq_register(struct platform_device *pdev,
+ struct regulator_dev *rdev,
+ const struct mt6360_irq_mapping *tbls,
+ int tbl_size)
+{
+ int i, irq, ret;
+
+ for (i = 0; i < tbl_size; i++) {
+ const struct mt6360_irq_mapping *irq_desc = tbls + i;
+
+ irq = platform_get_irq_byname(pdev, irq_desc->name);
+ if (irq < 0) {
+ dev_err(&pdev->dev,
+ "Fail to get %s irq\n", irq_desc->name);
+ return irq;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ irq_desc->handler,
+ IRQF_TRIGGER_NONE,
+ irq_desc->name,
+ rdev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Fail to request %s irq\n", irq_desc->name);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int mt6360_regulator_probe(struct platform_device *pdev)
+{
+ struct mt6360_regulator_data *mrd;
+ struct regulator_config config = {};
+ int i, ret;
+
+ mrd = devm_kzalloc(&pdev->dev, sizeof(*mrd), GFP_KERNEL);
+ if (!mrd)
+ return -ENOMEM;
+
+ mrd->dev = &pdev->dev;
+
+ mrd->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!mrd->regmap) {
+ dev_err(&pdev->dev, "Failed to get parent regmap\n");
+ return -ENODEV;
+ }
+
+ config.dev = &pdev->dev;
+ config.driver_data = mrd;
+ config.regmap = mrd->regmap;
+
+ for (i = 0; i < ARRAY_SIZE(mt6360_regulator_descs); i++) {
+ const struct mt6360_regulator_desc *rdesc =
+ mt6360_regulator_descs + i;
+ struct regulator_dev *rdev;
+
+ rdev = devm_regulator_register(&pdev->dev,
+ &rdesc->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev,
+ "Failed to register %d regulaotr\n", i);
+ return PTR_ERR(rdev);
+ }
+
+ ret = mt6360_regulator_irq_register(pdev, rdev,
+ rdesc->irq_tables,
+ rdesc->irq_table_size);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to register %d regulaotr irqs\n", i);
+ return ret;
+ }
+ }
+
+ platform_set_drvdata(pdev, mrd);
+
+ return 0;
+}
+
+static const struct of_device_id __maybe_unused mt6360_regulator_of_id[] = {
+ { .compatible = "mediatek,mt6360-regulator", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt6360_regulator_of_id);
+
+static struct platform_driver mt6360_regulator_driver = {
+ .driver = {
+ .name = "mt6360-regulator",
+ .of_match_table = mt6360_regulator_of_id,
+ },
+ .probe = mt6360_regulator_probe,
+};
+module_platform_driver(mt6360_regulator_driver);
+
+MODULE_AUTHOR("Gene Chen <gene_chen@richtek.com>");
+MODULE_DESCRIPTION("MT6360 Regulator Driver");
+MODULE_LICENSE("GPL v2");
--
2.7.4
_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek
^ permalink raw reply related [flat|nested] 10+ messages in thread