mlxreg-io.c (7183B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Mellanox register access driver 4 * 5 * Copyright (C) 2018 Mellanox Technologies 6 * Copyright (C) 2018 Vadim Pasternak <vadimp@mellanox.com> 7 */ 8 9#include <linux/bitops.h> 10#include <linux/device.h> 11#include <linux/hwmon.h> 12#include <linux/hwmon-sysfs.h> 13#include <linux/module.h> 14#include <linux/of_device.h> 15#include <linux/platform_data/mlxreg.h> 16#include <linux/platform_device.h> 17#include <linux/regmap.h> 18 19/* Attribute parameters. */ 20#define MLXREG_IO_ATT_SIZE 10 21#define MLXREG_IO_ATT_NUM 96 22 23/** 24 * struct mlxreg_io_priv_data - driver's private data: 25 * 26 * @pdev: platform device; 27 * @pdata: platform data; 28 * @hwmon: hwmon device; 29 * @mlxreg_io_attr: sysfs attributes array; 30 * @mlxreg_io_dev_attr: sysfs sensor device attribute array; 31 * @group: sysfs attribute group; 32 * @groups: list of sysfs attribute group for hwmon registration; 33 * @regsize: size of a register value; 34 */ 35struct mlxreg_io_priv_data { 36 struct platform_device *pdev; 37 struct mlxreg_core_platform_data *pdata; 38 struct device *hwmon; 39 struct attribute *mlxreg_io_attr[MLXREG_IO_ATT_NUM + 1]; 40 struct sensor_device_attribute mlxreg_io_dev_attr[MLXREG_IO_ATT_NUM]; 41 struct attribute_group group; 42 const struct attribute_group *groups[2]; 43 int regsize; 44}; 45 46static int 47mlxreg_io_get_reg(void *regmap, struct mlxreg_core_data *data, u32 in_val, 48 bool rw_flag, int regsize, u32 *regval) 49{ 50 int i, val, ret; 51 52 ret = regmap_read(regmap, data->reg, regval); 53 if (ret) 54 goto access_error; 55 56 /* 57 * There are four kinds of attributes: single bit, full register's 58 * bits, bit sequence, bits in few registers For the first kind field 59 * mask indicates which bits are not related and field bit is set zero. 60 * For the second kind field mask is set to zero and field bit is set 61 * with all bits one. No special handling for such kind of attributes - 62 * pass value as is. For the third kind, the field mask indicates which 63 * bits are related and the field bit is set to the first bit number 64 * (from 1 to 32) is the bit sequence. For the fourth kind - the number 65 * of registers which should be read for getting an attribute are 66 * specified through 'data->regnum' field. 67 */ 68 if (!data->bit) { 69 /* Single bit. */ 70 if (rw_flag) { 71 /* For show: expose effective bit value as 0 or 1. */ 72 *regval = !!(*regval & ~data->mask); 73 } else { 74 /* For store: set effective bit value. */ 75 *regval &= data->mask; 76 if (in_val) 77 *regval |= ~data->mask; 78 } 79 } else if (data->mask) { 80 /* Bit sequence. */ 81 if (rw_flag) { 82 /* For show: mask and shift right. */ 83 *regval = ror32(*regval & data->mask, (data->bit - 1)); 84 } else { 85 /* For store: shift to the position and mask. */ 86 in_val = rol32(in_val, data->bit - 1) & data->mask; 87 /* Clear relevant bits and set them to new value. */ 88 *regval = (*regval & ~data->mask) | in_val; 89 } 90 } else { 91 /* 92 * Some attributes could occupied few registers in case regmap 93 * bit size is 8 or 16. Compose such attributes from 'regnum' 94 * registers. Such attributes contain read-only data. 95 */ 96 for (i = 1; i < data->regnum; i++) { 97 ret = regmap_read(regmap, data->reg + i, &val); 98 if (ret) 99 goto access_error; 100 101 *regval |= rol32(val, regsize * i * 8); 102 } 103 } 104 105access_error: 106 return ret; 107} 108 109static ssize_t 110mlxreg_io_attr_show(struct device *dev, struct device_attribute *attr, 111 char *buf) 112{ 113 struct mlxreg_io_priv_data *priv = dev_get_drvdata(dev); 114 int index = to_sensor_dev_attr(attr)->index; 115 struct mlxreg_core_data *data = priv->pdata->data + index; 116 u32 regval = 0; 117 int ret; 118 119 ret = mlxreg_io_get_reg(priv->pdata->regmap, data, 0, true, 120 priv->regsize, ®val); 121 if (ret) 122 goto access_error; 123 124 return sprintf(buf, "%u\n", regval); 125 126access_error: 127 return ret; 128} 129 130static ssize_t 131mlxreg_io_attr_store(struct device *dev, struct device_attribute *attr, 132 const char *buf, size_t len) 133{ 134 struct mlxreg_io_priv_data *priv = dev_get_drvdata(dev); 135 int index = to_sensor_dev_attr(attr)->index; 136 struct mlxreg_core_data *data = priv->pdata->data + index; 137 u32 input_val, regval; 138 int ret; 139 140 if (len > MLXREG_IO_ATT_SIZE) 141 return -EINVAL; 142 143 /* Convert buffer to input value. */ 144 ret = kstrtou32(buf, 0, &input_val); 145 if (ret) 146 return ret; 147 148 ret = mlxreg_io_get_reg(priv->pdata->regmap, data, input_val, false, 149 priv->regsize, ®val); 150 if (ret) 151 goto access_error; 152 153 ret = regmap_write(priv->pdata->regmap, data->reg, regval); 154 if (ret) 155 goto access_error; 156 157 return len; 158 159access_error: 160 dev_err(&priv->pdev->dev, "Bus access error\n"); 161 return ret; 162} 163 164static struct device_attribute mlxreg_io_devattr_rw = { 165 .show = mlxreg_io_attr_show, 166 .store = mlxreg_io_attr_store, 167}; 168 169static int mlxreg_io_attr_init(struct mlxreg_io_priv_data *priv) 170{ 171 int i; 172 173 priv->group.attrs = devm_kcalloc(&priv->pdev->dev, 174 priv->pdata->counter, 175 sizeof(struct attribute *), 176 GFP_KERNEL); 177 if (!priv->group.attrs) 178 return -ENOMEM; 179 180 for (i = 0; i < priv->pdata->counter; i++) { 181 priv->mlxreg_io_attr[i] = 182 &priv->mlxreg_io_dev_attr[i].dev_attr.attr; 183 memcpy(&priv->mlxreg_io_dev_attr[i].dev_attr, 184 &mlxreg_io_devattr_rw, sizeof(struct device_attribute)); 185 186 /* Set attribute name as a label. */ 187 priv->mlxreg_io_attr[i]->name = 188 devm_kasprintf(&priv->pdev->dev, GFP_KERNEL, 189 priv->pdata->data[i].label); 190 191 if (!priv->mlxreg_io_attr[i]->name) { 192 dev_err(&priv->pdev->dev, "Memory allocation failed for sysfs attribute %d.\n", 193 i + 1); 194 return -ENOMEM; 195 } 196 197 priv->mlxreg_io_dev_attr[i].dev_attr.attr.mode = 198 priv->pdata->data[i].mode; 199 priv->mlxreg_io_dev_attr[i].dev_attr.attr.name = 200 priv->mlxreg_io_attr[i]->name; 201 priv->mlxreg_io_dev_attr[i].index = i; 202 sysfs_attr_init(&priv->mlxreg_io_dev_attr[i].dev_attr.attr); 203 } 204 205 priv->group.attrs = priv->mlxreg_io_attr; 206 priv->groups[0] = &priv->group; 207 priv->groups[1] = NULL; 208 209 return 0; 210} 211 212static int mlxreg_io_probe(struct platform_device *pdev) 213{ 214 struct mlxreg_io_priv_data *priv; 215 int err; 216 217 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 218 if (!priv) 219 return -ENOMEM; 220 221 priv->pdata = dev_get_platdata(&pdev->dev); 222 if (!priv->pdata) { 223 dev_err(&pdev->dev, "Failed to get platform data.\n"); 224 return -EINVAL; 225 } 226 227 priv->pdev = pdev; 228 priv->regsize = regmap_get_val_bytes(priv->pdata->regmap); 229 if (priv->regsize < 0) 230 return priv->regsize; 231 232 err = mlxreg_io_attr_init(priv); 233 if (err) { 234 dev_err(&priv->pdev->dev, "Failed to allocate attributes: %d\n", 235 err); 236 return err; 237 } 238 239 priv->hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, 240 "mlxreg_io", 241 priv, 242 priv->groups); 243 if (IS_ERR(priv->hwmon)) { 244 dev_err(&pdev->dev, "Failed to register hwmon device %ld\n", 245 PTR_ERR(priv->hwmon)); 246 return PTR_ERR(priv->hwmon); 247 } 248 249 dev_set_drvdata(&pdev->dev, priv); 250 251 return 0; 252} 253 254static struct platform_driver mlxreg_io_driver = { 255 .driver = { 256 .name = "mlxreg-io", 257 }, 258 .probe = mlxreg_io_probe, 259}; 260 261module_platform_driver(mlxreg_io_driver); 262 263MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); 264MODULE_DESCRIPTION("Mellanox regmap I/O access driver"); 265MODULE_LICENSE("GPL"); 266MODULE_ALIAS("platform:mlxreg-io");