1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Broadcom BCM590xx PMU 4 * 5 * Copyright 2014 Linaro Limited 6 * Author: Matt Porter <mporter@linaro.org> 7 */ 8 9 #include <linux/err.h> 10 #include <linux/i2c.h> 11 #include <linux/init.h> 12 #include <linux/mfd/bcm590xx.h> 13 #include <linux/mfd/core.h> 14 #include <linux/module.h> 15 #include <linux/moduleparam.h> 16 #include <linux/of.h> 17 #include <linux/regmap.h> 18 #include <linux/slab.h> 19 20 /* Under primary I2C address: */ 21 #define BCM590XX_REG_PMUID 0x1e 22 23 #define BCM590XX_REG_PMUREV 0x1f 24 #define BCM590XX_PMUREV_DIG_MASK 0xF 25 #define BCM590XX_PMUREV_DIG_SHIFT 0 26 #define BCM590XX_PMUREV_ANA_MASK 0xF0 27 #define BCM590XX_PMUREV_ANA_SHIFT 4 28 29 static const struct mfd_cell bcm590xx_devs[] = { 30 { 31 .name = "bcm590xx-vregs", 32 }, 33 }; 34 35 static const struct regmap_config bcm590xx_regmap_config_pri = { 36 .reg_bits = 8, 37 .val_bits = 8, 38 .max_register = BCM590XX_MAX_REGISTER_PRI, 39 .cache_type = REGCACHE_MAPLE, 40 }; 41 42 static const struct regmap_config bcm590xx_regmap_config_sec = { 43 .reg_bits = 8, 44 .val_bits = 8, 45 .max_register = BCM590XX_MAX_REGISTER_SEC, 46 .cache_type = REGCACHE_MAPLE, 47 }; 48 49 /* Map PMU ID value to model name string */ 50 static const char * const bcm590xx_names[] = { 51 [BCM590XX_PMUID_BCM59054] = "BCM59054", 52 [BCM590XX_PMUID_BCM59056] = "BCM59056", 53 }; 54 55 static int bcm590xx_parse_version(struct bcm590xx *bcm590xx) 56 { 57 unsigned int id, rev; 58 int ret; 59 60 /* Get PMU ID and verify that it matches compatible */ 61 ret = regmap_read(bcm590xx->regmap_pri, BCM590XX_REG_PMUID, &id); 62 if (ret) { 63 dev_err(bcm590xx->dev, "failed to read PMU ID: %d\n", ret); 64 return ret; 65 } 66 67 if (id != bcm590xx->pmu_id) { 68 dev_err(bcm590xx->dev, "Incorrect ID for %s: expected %x, got %x.\n", 69 bcm590xx_names[bcm590xx->pmu_id], bcm590xx->pmu_id, id); 70 return -ENODEV; 71 } 72 73 /* Get PMU revision and store it in the info struct */ 74 ret = regmap_read(bcm590xx->regmap_pri, BCM590XX_REG_PMUREV, &rev); 75 if (ret) { 76 dev_err(bcm590xx->dev, "failed to read PMU revision: %d\n", ret); 77 return ret; 78 } 79 80 bcm590xx->rev_digital = (rev & BCM590XX_PMUREV_DIG_MASK) >> BCM590XX_PMUREV_DIG_SHIFT; 81 82 bcm590xx->rev_analog = (rev & BCM590XX_PMUREV_ANA_MASK) >> BCM590XX_PMUREV_ANA_SHIFT; 83 84 dev_dbg(bcm590xx->dev, "PMU ID 0x%x (%s), revision: digital %d, analog %d", 85 id, bcm590xx_names[id], bcm590xx->rev_digital, bcm590xx->rev_analog); 86 87 return 0; 88 } 89 90 static int bcm590xx_i2c_probe(struct i2c_client *i2c_pri) 91 { 92 struct bcm590xx *bcm590xx; 93 int ret; 94 95 bcm590xx = devm_kzalloc(&i2c_pri->dev, sizeof(*bcm590xx), GFP_KERNEL); 96 if (!bcm590xx) 97 return -ENOMEM; 98 99 i2c_set_clientdata(i2c_pri, bcm590xx); 100 bcm590xx->dev = &i2c_pri->dev; 101 bcm590xx->i2c_pri = i2c_pri; 102 103 bcm590xx->pmu_id = (uintptr_t) of_device_get_match_data(bcm590xx->dev); 104 105 bcm590xx->regmap_pri = devm_regmap_init_i2c(i2c_pri, 106 &bcm590xx_regmap_config_pri); 107 if (IS_ERR(bcm590xx->regmap_pri)) { 108 ret = PTR_ERR(bcm590xx->regmap_pri); 109 dev_err(&i2c_pri->dev, "primary regmap init failed: %d\n", ret); 110 return ret; 111 } 112 113 /* Secondary I2C slave address is the base address with A(2) asserted */ 114 bcm590xx->i2c_sec = i2c_new_dummy_device(i2c_pri->adapter, 115 i2c_pri->addr | BIT(2)); 116 if (IS_ERR(bcm590xx->i2c_sec)) { 117 dev_err(&i2c_pri->dev, "failed to add secondary I2C device\n"); 118 return PTR_ERR(bcm590xx->i2c_sec); 119 } 120 i2c_set_clientdata(bcm590xx->i2c_sec, bcm590xx); 121 122 bcm590xx->regmap_sec = devm_regmap_init_i2c(bcm590xx->i2c_sec, 123 &bcm590xx_regmap_config_sec); 124 if (IS_ERR(bcm590xx->regmap_sec)) { 125 ret = PTR_ERR(bcm590xx->regmap_sec); 126 dev_err(&bcm590xx->i2c_sec->dev, 127 "secondary regmap init failed: %d\n", ret); 128 goto err; 129 } 130 131 ret = bcm590xx_parse_version(bcm590xx); 132 if (ret) 133 goto err; 134 135 ret = devm_mfd_add_devices(&i2c_pri->dev, -1, bcm590xx_devs, 136 ARRAY_SIZE(bcm590xx_devs), NULL, 0, NULL); 137 if (ret < 0) { 138 dev_err(&i2c_pri->dev, "failed to add sub-devices: %d\n", ret); 139 goto err; 140 } 141 142 return 0; 143 144 err: 145 i2c_unregister_device(bcm590xx->i2c_sec); 146 return ret; 147 } 148 149 static const struct of_device_id bcm590xx_of_match[] = { 150 { 151 .compatible = "brcm,bcm59054", 152 .data = (void *)BCM590XX_PMUID_BCM59054, 153 }, 154 { 155 .compatible = "brcm,bcm59056", 156 .data = (void *)BCM590XX_PMUID_BCM59056, 157 }, 158 { } 159 }; 160 MODULE_DEVICE_TABLE(of, bcm590xx_of_match); 161 162 static const struct i2c_device_id bcm590xx_i2c_id[] = { 163 { "bcm59054" }, 164 { "bcm59056" }, 165 { } 166 }; 167 MODULE_DEVICE_TABLE(i2c, bcm590xx_i2c_id); 168 169 static struct i2c_driver bcm590xx_i2c_driver = { 170 .driver = { 171 .name = "bcm590xx", 172 .of_match_table = bcm590xx_of_match, 173 }, 174 .probe = bcm590xx_i2c_probe, 175 .id_table = bcm590xx_i2c_id, 176 }; 177 module_i2c_driver(bcm590xx_i2c_driver); 178 179 MODULE_AUTHOR("Matt Porter <mporter@linaro.org>"); 180 MODULE_DESCRIPTION("BCM590xx multi-function driver"); 181 MODULE_LICENSE("GPL v2"); 182