| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
|---|
| 1 | 2 | /* |
|---|
| 2 | | - * Copyright (c) 2016-2018 Mellanox Technologies. All rights reserved. |
|---|
| 3 | | - * Copyright (c) 2016-2018 Vadim Pasternak <vadimp@mellanox.com> |
|---|
| 3 | + * Mellanox hotplug driver |
|---|
| 4 | 4 | * |
|---|
| 5 | | - * Redistribution and use in source and binary forms, with or without |
|---|
| 6 | | - * modification, are permitted provided that the following conditions are met: |
|---|
| 7 | | - * |
|---|
| 8 | | - * 1. Redistributions of source code must retain the above copyright |
|---|
| 9 | | - * notice, this list of conditions and the following disclaimer. |
|---|
| 10 | | - * 2. Redistributions in binary form must reproduce the above copyright |
|---|
| 11 | | - * notice, this list of conditions and the following disclaimer in the |
|---|
| 12 | | - * documentation and/or other materials provided with the distribution. |
|---|
| 13 | | - * 3. Neither the names of the copyright holders nor the names of its |
|---|
| 14 | | - * contributors may be used to endorse or promote products derived from |
|---|
| 15 | | - * this software without specific prior written permission. |
|---|
| 16 | | - * |
|---|
| 17 | | - * Alternatively, this software may be distributed under the terms of the |
|---|
| 18 | | - * GNU General Public License ("GPL") version 2 as published by the Free |
|---|
| 19 | | - * Software Foundation. |
|---|
| 20 | | - * |
|---|
| 21 | | - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|---|
| 22 | | - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|---|
| 23 | | - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|---|
| 24 | | - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
|---|
| 25 | | - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|---|
| 26 | | - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|---|
| 27 | | - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|---|
| 28 | | - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|---|
| 29 | | - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|---|
| 30 | | - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|---|
| 31 | | - * POSSIBILITY OF SUCH DAMAGE. |
|---|
| 5 | + * Copyright (C) 2016-2020 Mellanox Technologies |
|---|
| 32 | 6 | */ |
|---|
| 33 | 7 | |
|---|
| 34 | 8 | #include <linux/bitops.h> |
|---|
| .. | .. |
|---|
| 42 | 16 | #include <linux/platform_data/mlxreg.h> |
|---|
| 43 | 17 | #include <linux/platform_device.h> |
|---|
| 44 | 18 | #include <linux/spinlock.h> |
|---|
| 19 | +#include <linux/string_helpers.h> |
|---|
| 45 | 20 | #include <linux/regmap.h> |
|---|
| 46 | 21 | #include <linux/workqueue.h> |
|---|
| 47 | 22 | |
|---|
| .. | .. |
|---|
| 97 | 72 | u8 not_asserted; |
|---|
| 98 | 73 | }; |
|---|
| 99 | 74 | |
|---|
| 75 | +/* Environment variables array for udev. */ |
|---|
| 76 | +static char *mlxreg_hotplug_udev_envp[] = { NULL, NULL }; |
|---|
| 77 | + |
|---|
| 78 | +static int |
|---|
| 79 | +mlxreg_hotplug_udev_event_send(struct kobject *kobj, |
|---|
| 80 | + struct mlxreg_core_data *data, bool action) |
|---|
| 81 | +{ |
|---|
| 82 | + char event_str[MLXREG_CORE_LABEL_MAX_SIZE + 2]; |
|---|
| 83 | + char label[MLXREG_CORE_LABEL_MAX_SIZE] = { 0 }; |
|---|
| 84 | + |
|---|
| 85 | + mlxreg_hotplug_udev_envp[0] = event_str; |
|---|
| 86 | + string_upper(label, data->label); |
|---|
| 87 | + snprintf(event_str, MLXREG_CORE_LABEL_MAX_SIZE, "%s=%d", label, !!action); |
|---|
| 88 | + |
|---|
| 89 | + return kobject_uevent_env(kobj, KOBJ_CHANGE, mlxreg_hotplug_udev_envp); |
|---|
| 90 | +} |
|---|
| 91 | + |
|---|
| 100 | 92 | static int mlxreg_hotplug_device_create(struct mlxreg_hotplug_priv_data *priv, |
|---|
| 101 | 93 | struct mlxreg_core_data *data) |
|---|
| 102 | 94 | { |
|---|
| 103 | 95 | struct mlxreg_core_hotplug_platform_data *pdata; |
|---|
| 96 | + struct i2c_client *client; |
|---|
| 104 | 97 | |
|---|
| 105 | 98 | /* Notify user by sending hwmon uevent. */ |
|---|
| 106 | | - kobject_uevent(&priv->hwmon->kobj, KOBJ_CHANGE); |
|---|
| 99 | + mlxreg_hotplug_udev_event_send(&priv->hwmon->kobj, data, true); |
|---|
| 107 | 100 | |
|---|
| 108 | 101 | /* |
|---|
| 109 | 102 | * Return if adapter number is negative. It could be in case hotplug |
|---|
| .. | .. |
|---|
| 121 | 114 | return -EFAULT; |
|---|
| 122 | 115 | } |
|---|
| 123 | 116 | |
|---|
| 124 | | - data->hpdev.client = i2c_new_device(data->hpdev.adapter, |
|---|
| 125 | | - data->hpdev.brdinfo); |
|---|
| 126 | | - if (!data->hpdev.client) { |
|---|
| 117 | + client = i2c_new_client_device(data->hpdev.adapter, |
|---|
| 118 | + data->hpdev.brdinfo); |
|---|
| 119 | + if (IS_ERR(client)) { |
|---|
| 127 | 120 | dev_err(priv->dev, "Failed to create client %s at bus %d at addr 0x%02x\n", |
|---|
| 128 | 121 | data->hpdev.brdinfo->type, data->hpdev.nr + |
|---|
| 129 | 122 | pdata->shift_nr, data->hpdev.brdinfo->addr); |
|---|
| 130 | 123 | |
|---|
| 131 | 124 | i2c_put_adapter(data->hpdev.adapter); |
|---|
| 132 | 125 | data->hpdev.adapter = NULL; |
|---|
| 133 | | - return -EFAULT; |
|---|
| 126 | + return PTR_ERR(client); |
|---|
| 134 | 127 | } |
|---|
| 128 | + |
|---|
| 129 | + data->hpdev.client = client; |
|---|
| 135 | 130 | |
|---|
| 136 | 131 | return 0; |
|---|
| 137 | 132 | } |
|---|
| .. | .. |
|---|
| 141 | 136 | struct mlxreg_core_data *data) |
|---|
| 142 | 137 | { |
|---|
| 143 | 138 | /* Notify user by sending hwmon uevent. */ |
|---|
| 144 | | - kobject_uevent(&priv->hwmon->kobj, KOBJ_CHANGE); |
|---|
| 139 | + mlxreg_hotplug_udev_event_send(&priv->hwmon->kobj, data, false); |
|---|
| 145 | 140 | |
|---|
| 146 | 141 | if (data->hpdev.client) { |
|---|
| 147 | 142 | i2c_unregister_device(data->hpdev.client); |
|---|
| .. | .. |
|---|
| 196 | 191 | struct mlxreg_core_hotplug_platform_data *pdata; |
|---|
| 197 | 192 | struct mlxreg_core_item *item; |
|---|
| 198 | 193 | struct mlxreg_core_data *data; |
|---|
| 199 | | - int num_attrs = 0, id = 0, i, j; |
|---|
| 194 | + unsigned long mask; |
|---|
| 195 | + u32 regval; |
|---|
| 196 | + int num_attrs = 0, id = 0, i, j, k, ret; |
|---|
| 200 | 197 | |
|---|
| 201 | 198 | pdata = dev_get_platdata(&priv->pdev->dev); |
|---|
| 202 | 199 | item = pdata->items; |
|---|
| 203 | 200 | |
|---|
| 204 | 201 | /* Go over all kinds of items - psu, pwr, fan. */ |
|---|
| 205 | 202 | for (i = 0; i < pdata->counter; i++, item++) { |
|---|
| 206 | | - num_attrs += item->count; |
|---|
| 203 | + if (item->capability) { |
|---|
| 204 | + /* |
|---|
| 205 | + * Read group capability register to get actual number |
|---|
| 206 | + * of interrupt capable components and set group mask |
|---|
| 207 | + * accordingly. |
|---|
| 208 | + */ |
|---|
| 209 | + ret = regmap_read(priv->regmap, item->capability, |
|---|
| 210 | + ®val); |
|---|
| 211 | + if (ret) |
|---|
| 212 | + return ret; |
|---|
| 213 | + |
|---|
| 214 | + item->mask = GENMASK((regval & item->mask) - 1, 0); |
|---|
| 215 | + } |
|---|
| 216 | + |
|---|
| 207 | 217 | data = item->data; |
|---|
| 208 | | - /* Go over all units within the item. */ |
|---|
| 209 | | - for (j = 0; j < item->count; j++, data++, id++) { |
|---|
| 218 | + |
|---|
| 219 | + /* Go over all unmasked units within item. */ |
|---|
| 220 | + mask = item->mask; |
|---|
| 221 | + k = 0; |
|---|
| 222 | + for_each_set_bit(j, &mask, item->count) { |
|---|
| 223 | + if (data->capability) { |
|---|
| 224 | + /* |
|---|
| 225 | + * Read capability register and skip non |
|---|
| 226 | + * relevant attributes. |
|---|
| 227 | + */ |
|---|
| 228 | + ret = regmap_read(priv->regmap, |
|---|
| 229 | + data->capability, ®val); |
|---|
| 230 | + if (ret) |
|---|
| 231 | + return ret; |
|---|
| 232 | + if (!(regval & data->bit)) { |
|---|
| 233 | + data++; |
|---|
| 234 | + continue; |
|---|
| 235 | + } |
|---|
| 236 | + } |
|---|
| 210 | 237 | PRIV_ATTR(id) = &PRIV_DEV_ATTR(id).dev_attr.attr; |
|---|
| 211 | 238 | PRIV_ATTR(id)->name = devm_kasprintf(&priv->pdev->dev, |
|---|
| 212 | 239 | GFP_KERNEL, |
|---|
| .. | .. |
|---|
| 224 | 251 | PRIV_DEV_ATTR(id).dev_attr.show = |
|---|
| 225 | 252 | mlxreg_hotplug_attr_show; |
|---|
| 226 | 253 | PRIV_DEV_ATTR(id).nr = i; |
|---|
| 227 | | - PRIV_DEV_ATTR(id).index = j; |
|---|
| 254 | + PRIV_DEV_ATTR(id).index = k; |
|---|
| 228 | 255 | sysfs_attr_init(&PRIV_DEV_ATTR(id).dev_attr.attr); |
|---|
| 256 | + data++; |
|---|
| 257 | + id++; |
|---|
| 258 | + k++; |
|---|
| 229 | 259 | } |
|---|
| 260 | + num_attrs += k; |
|---|
| 230 | 261 | } |
|---|
| 231 | 262 | |
|---|
| 232 | 263 | priv->group.attrs = devm_kcalloc(&priv->pdev->dev, |
|---|
| .. | .. |
|---|
| 496 | 527 | { |
|---|
| 497 | 528 | struct mlxreg_core_hotplug_platform_data *pdata; |
|---|
| 498 | 529 | struct mlxreg_core_item *item; |
|---|
| 499 | | - int i, ret; |
|---|
| 530 | + struct mlxreg_core_data *data; |
|---|
| 531 | + u32 regval; |
|---|
| 532 | + int i, j, ret; |
|---|
| 500 | 533 | |
|---|
| 501 | 534 | pdata = dev_get_platdata(&priv->pdev->dev); |
|---|
| 502 | 535 | item = pdata->items; |
|---|
| .. | .. |
|---|
| 507 | 540 | MLXREG_HOTPLUG_EVENT_OFF, 0); |
|---|
| 508 | 541 | if (ret) |
|---|
| 509 | 542 | goto out; |
|---|
| 543 | + |
|---|
| 544 | + /* |
|---|
| 545 | + * Verify if hardware configuration requires to disable |
|---|
| 546 | + * interrupt capability for some of components. |
|---|
| 547 | + */ |
|---|
| 548 | + data = item->data; |
|---|
| 549 | + for (j = 0; j < item->count; j++, data++) { |
|---|
| 550 | + /* Verify if the attribute has capability register. */ |
|---|
| 551 | + if (data->capability) { |
|---|
| 552 | + /* Read capability register. */ |
|---|
| 553 | + ret = regmap_read(priv->regmap, |
|---|
| 554 | + data->capability, ®val); |
|---|
| 555 | + if (ret) |
|---|
| 556 | + goto out; |
|---|
| 557 | + |
|---|
| 558 | + if (!(regval & data->bit)) |
|---|
| 559 | + item->mask &= ~BIT(j); |
|---|
| 560 | + } |
|---|
| 561 | + } |
|---|
| 510 | 562 | |
|---|
| 511 | 563 | /* Set group initial status as mask and unmask group event. */ |
|---|
| 512 | 564 | if (item->inversed) { |
|---|
| .. | .. |
|---|
| 621 | 673 | priv->irq = pdata->irq; |
|---|
| 622 | 674 | } else { |
|---|
| 623 | 675 | priv->irq = platform_get_irq(pdev, 0); |
|---|
| 624 | | - if (priv->irq < 0) { |
|---|
| 625 | | - dev_err(&pdev->dev, "Failed to get platform irq: %d\n", |
|---|
| 626 | | - priv->irq); |
|---|
| 676 | + if (priv->irq < 0) |
|---|
| 627 | 677 | return priv->irq; |
|---|
| 628 | | - } |
|---|
| 629 | 678 | } |
|---|
| 630 | 679 | |
|---|
| 631 | 680 | priv->regmap = pdata->regmap; |
|---|