1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright (C) 2022 Hewlett-Packard Enterprise Development Company, L.P. */ 3 4 #include <linux/bits.h> 5 #include <linux/err.h> 6 #include <linux/hwmon.h> 7 #include <linux/io.h> 8 #include <linux/module.h> 9 #include <linux/platform_device.h> 10 11 #define OFS_FAN_INST 0 /* Is 0 because plreg base will be set at INST */ 12 #define OFS_FAN_FAIL 2 /* Is 2 bytes after base */ 13 #define OFS_SEVSTAT 0 /* Is 0 because fn2 base will be set at SEVSTAT */ 14 #define POWER_BIT 24 15 16 struct gxp_fan_ctrl_drvdata { 17 void __iomem *base; 18 void __iomem *plreg; 19 void __iomem *fn2; 20 }; 21 22 static bool fan_installed(struct device *dev, int fan) 23 { 24 struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); 25 u8 val; 26 27 val = readb(drvdata->plreg + OFS_FAN_INST); 28 29 return !!(val & BIT(fan)); 30 } 31 32 static long fan_failed(struct device *dev, int fan) 33 { 34 struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); 35 u8 val; 36 37 val = readb(drvdata->plreg + OFS_FAN_FAIL); 38 39 return !!(val & BIT(fan)); 40 } 41 42 static long fan_enabled(struct device *dev, int fan) 43 { 44 struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); 45 u32 val; 46 47 /* 48 * Check the power status as if the platform is off the value 49 * reported for the PWM will be incorrect. Report fan as 50 * disabled. 51 */ 52 val = readl(drvdata->fn2 + OFS_SEVSTAT); 53 54 return !!((val & BIT(POWER_BIT)) && fan_installed(dev, fan)); 55 } 56 57 static int gxp_pwm_write(struct device *dev, u32 attr, int channel, long val) 58 { 59 struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); 60 61 switch (attr) { 62 case hwmon_pwm_input: 63 if (val > 255 || val < 0) 64 return -EINVAL; 65 writeb(val, drvdata->base + channel); 66 return 0; 67 default: 68 return -EOPNOTSUPP; 69 } 70 } 71 72 static int gxp_fan_ctrl_write(struct device *dev, enum hwmon_sensor_types type, 73 u32 attr, int channel, long val) 74 { 75 switch (type) { 76 case hwmon_pwm: 77 return gxp_pwm_write(dev, attr, channel, val); 78 default: 79 return -EOPNOTSUPP; 80 } 81 } 82 83 static int gxp_fan_read(struct device *dev, u32 attr, int channel, long *val) 84 { 85 switch (attr) { 86 case hwmon_fan_enable: 87 *val = fan_enabled(dev, channel); 88 return 0; 89 case hwmon_fan_fault: 90 *val = fan_failed(dev, channel); 91 return 0; 92 default: 93 return -EOPNOTSUPP; 94 } 95 } 96 97 static int gxp_pwm_read(struct device *dev, u32 attr, int channel, long *val) 98 { 99 struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); 100 u32 reg; 101 102 /* 103 * Check the power status of the platform. If the platform is off 104 * the value reported for the PWM will be incorrect. In this case 105 * report a PWM of zero. 106 */ 107 108 reg = readl(drvdata->fn2 + OFS_SEVSTAT); 109 110 if (reg & BIT(POWER_BIT)) 111 *val = fan_installed(dev, channel) ? readb(drvdata->base + channel) : 0; 112 else 113 *val = 0; 114 115 return 0; 116 } 117 118 static int gxp_fan_ctrl_read(struct device *dev, enum hwmon_sensor_types type, 119 u32 attr, int channel, long *val) 120 { 121 switch (type) { 122 case hwmon_fan: 123 return gxp_fan_read(dev, attr, channel, val); 124 case hwmon_pwm: 125 return gxp_pwm_read(dev, attr, channel, val); 126 default: 127 return -EOPNOTSUPP; 128 } 129 } 130 131 static umode_t gxp_fan_ctrl_is_visible(const void *_data, 132 enum hwmon_sensor_types type, 133 u32 attr, int channel) 134 { 135 umode_t mode = 0; 136 137 switch (type) { 138 case hwmon_fan: 139 switch (attr) { 140 case hwmon_fan_enable: 141 case hwmon_fan_fault: 142 mode = 0444; 143 break; 144 default: 145 break; 146 } 147 break; 148 case hwmon_pwm: 149 switch (attr) { 150 case hwmon_pwm_input: 151 mode = 0644; 152 break; 153 default: 154 break; 155 } 156 break; 157 default: 158 break; 159 } 160 161 return mode; 162 } 163 164 static const struct hwmon_ops gxp_fan_ctrl_ops = { 165 .is_visible = gxp_fan_ctrl_is_visible, 166 .read = gxp_fan_ctrl_read, 167 .write = gxp_fan_ctrl_write, 168 }; 169 170 static const struct hwmon_channel_info * const gxp_fan_ctrl_info[] = { 171 HWMON_CHANNEL_INFO(fan, 172 HWMON_F_FAULT | HWMON_F_ENABLE, 173 HWMON_F_FAULT | HWMON_F_ENABLE, 174 HWMON_F_FAULT | HWMON_F_ENABLE, 175 HWMON_F_FAULT | HWMON_F_ENABLE, 176 HWMON_F_FAULT | HWMON_F_ENABLE, 177 HWMON_F_FAULT | HWMON_F_ENABLE, 178 HWMON_F_FAULT | HWMON_F_ENABLE, 179 HWMON_F_FAULT | HWMON_F_ENABLE), 180 HWMON_CHANNEL_INFO(pwm, 181 HWMON_PWM_INPUT, 182 HWMON_PWM_INPUT, 183 HWMON_PWM_INPUT, 184 HWMON_PWM_INPUT, 185 HWMON_PWM_INPUT, 186 HWMON_PWM_INPUT, 187 HWMON_PWM_INPUT, 188 HWMON_PWM_INPUT), 189 NULL 190 }; 191 192 static const struct hwmon_chip_info gxp_fan_ctrl_chip_info = { 193 .ops = &gxp_fan_ctrl_ops, 194 .info = gxp_fan_ctrl_info, 195 196 }; 197 198 static int gxp_fan_ctrl_probe(struct platform_device *pdev) 199 { 200 struct gxp_fan_ctrl_drvdata *drvdata; 201 struct device *dev = &pdev->dev; 202 struct device *hwmon_dev; 203 204 drvdata = devm_kzalloc(dev, sizeof(struct gxp_fan_ctrl_drvdata), 205 GFP_KERNEL); 206 if (!drvdata) 207 return -ENOMEM; 208 209 drvdata->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); 210 if (IS_ERR(drvdata->base)) 211 return dev_err_probe(dev, PTR_ERR(drvdata->base), 212 "failed to map base\n"); 213 214 drvdata->plreg = devm_platform_ioremap_resource_byname(pdev, 215 "pl"); 216 if (IS_ERR(drvdata->plreg)) 217 return dev_err_probe(dev, PTR_ERR(drvdata->plreg), 218 "failed to map plreg\n"); 219 220 drvdata->fn2 = devm_platform_ioremap_resource_byname(pdev, 221 "fn2"); 222 if (IS_ERR(drvdata->fn2)) 223 return dev_err_probe(dev, PTR_ERR(drvdata->fn2), 224 "failed to map fn2\n"); 225 226 hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, 227 "hpe_gxp_fan_ctrl", 228 drvdata, 229 &gxp_fan_ctrl_chip_info, 230 NULL); 231 232 return PTR_ERR_OR_ZERO(hwmon_dev); 233 } 234 235 static const struct of_device_id gxp_fan_ctrl_of_match[] = { 236 { .compatible = "hpe,gxp-fan-ctrl", }, 237 {}, 238 }; 239 MODULE_DEVICE_TABLE(of, gxp_fan_ctrl_of_match); 240 241 static struct platform_driver gxp_fan_ctrl_driver = { 242 .probe = gxp_fan_ctrl_probe, 243 .driver = { 244 .name = "gxp-fan-ctrl", 245 .of_match_table = gxp_fan_ctrl_of_match, 246 }, 247 }; 248 module_platform_driver(gxp_fan_ctrl_driver); 249 250 MODULE_AUTHOR("Nick Hawkins <nick.hawkins@hpe.com>"); 251 MODULE_DESCRIPTION("HPE GXP fan controller"); 252 MODULE_LICENSE("GPL"); 253