linux/drivers/platform/mellanox/mlxreg-io.c
<<
>>
Prefs
   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       48
  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, &regval);
 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, &regval);
 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");
 267