linux/drivers/i2c/i2c-smbus.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * i2c-smbus.c - SMBus extensions to the I2C protocol
   4 *
   5 * Copyright (C) 2008 David Brownell
   6 * Copyright (C) 2010 Jean Delvare <jdelvare@suse.de>
   7 */
   8
   9#include <linux/device.h>
  10#include <linux/i2c.h>
  11#include <linux/i2c-smbus.h>
  12#include <linux/interrupt.h>
  13#include <linux/kernel.h>
  14#include <linux/module.h>
  15#include <linux/of_irq.h>
  16#include <linux/slab.h>
  17#include <linux/workqueue.h>
  18
  19struct i2c_smbus_alert {
  20        struct work_struct      alert;
  21        struct i2c_client       *ara;           /* Alert response address */
  22};
  23
  24struct alert_data {
  25        unsigned short          addr;
  26        enum i2c_alert_protocol type;
  27        unsigned int            data;
  28};
  29
  30/* If this is the alerting device, notify its driver */
  31static int smbus_do_alert(struct device *dev, void *addrp)
  32{
  33        struct i2c_client *client = i2c_verify_client(dev);
  34        struct alert_data *data = addrp;
  35        struct i2c_driver *driver;
  36
  37        if (!client || client->addr != data->addr)
  38                return 0;
  39        if (client->flags & I2C_CLIENT_TEN)
  40                return 0;
  41
  42        /*
  43         * Drivers should either disable alerts, or provide at least
  44         * a minimal handler.  Lock so the driver won't change.
  45         */
  46        device_lock(dev);
  47        if (client->dev.driver) {
  48                driver = to_i2c_driver(client->dev.driver);
  49                if (driver->alert)
  50                        driver->alert(client, data->type, data->data);
  51                else
  52                        dev_warn(&client->dev, "no driver alert()!\n");
  53        } else
  54                dev_dbg(&client->dev, "alert with no driver\n");
  55        device_unlock(dev);
  56
  57        /* Stop iterating after we find the device */
  58        return -EBUSY;
  59}
  60
  61/*
  62 * The alert IRQ handler needs to hand work off to a task which can issue
  63 * SMBus calls, because those sleeping calls can't be made in IRQ context.
  64 */
  65static irqreturn_t smbus_alert(int irq, void *d)
  66{
  67        struct i2c_smbus_alert *alert = d;
  68        struct i2c_client *ara;
  69        unsigned short prev_addr = 0;   /* Not a valid address */
  70
  71        ara = alert->ara;
  72
  73        for (;;) {
  74                s32 status;
  75                struct alert_data data;
  76
  77                /*
  78                 * Devices with pending alerts reply in address order, low
  79                 * to high, because of slave transmit arbitration.  After
  80                 * responding, an SMBus device stops asserting SMBALERT#.
  81                 *
  82                 * Note that SMBus 2.0 reserves 10-bit addresses for future
  83                 * use.  We neither handle them, nor try to use PEC here.
  84                 */
  85                status = i2c_smbus_read_byte(ara);
  86                if (status < 0)
  87                        break;
  88
  89                data.data = status & 1;
  90                data.addr = status >> 1;
  91                data.type = I2C_PROTOCOL_SMBUS_ALERT;
  92
  93                if (data.addr == prev_addr) {
  94                        dev_warn(&ara->dev, "Duplicate SMBALERT# from dev "
  95                                "0x%02x, skipping\n", data.addr);
  96                        break;
  97                }
  98                dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n",
  99                        data.addr, data.data);
 100
 101                /* Notify driver for the device which issued the alert */
 102                device_for_each_child(&ara->adapter->dev, &data,
 103                                      smbus_do_alert);
 104                prev_addr = data.addr;
 105        }
 106
 107        return IRQ_HANDLED;
 108}
 109
 110static void smbalert_work(struct work_struct *work)
 111{
 112        struct i2c_smbus_alert *alert;
 113
 114        alert = container_of(work, struct i2c_smbus_alert, alert);
 115
 116        smbus_alert(0, alert);
 117
 118}
 119
 120/* Setup SMBALERT# infrastructure */
 121static int smbalert_probe(struct i2c_client *ara,
 122                          const struct i2c_device_id *id)
 123{
 124        struct i2c_smbus_alert_setup *setup = dev_get_platdata(&ara->dev);
 125        struct i2c_smbus_alert *alert;
 126        struct i2c_adapter *adapter = ara->adapter;
 127        int res, irq;
 128
 129        alert = devm_kzalloc(&ara->dev, sizeof(struct i2c_smbus_alert),
 130                             GFP_KERNEL);
 131        if (!alert)
 132                return -ENOMEM;
 133
 134        if (setup) {
 135                irq = setup->irq;
 136        } else {
 137                irq = of_irq_get_byname(adapter->dev.of_node, "smbus_alert");
 138                if (irq <= 0)
 139                        return irq;
 140        }
 141
 142        INIT_WORK(&alert->alert, smbalert_work);
 143        alert->ara = ara;
 144
 145        if (irq > 0) {
 146                res = devm_request_threaded_irq(&ara->dev, irq,
 147                                                NULL, smbus_alert,
 148                                                IRQF_SHARED | IRQF_ONESHOT,
 149                                                "smbus_alert", alert);
 150                if (res)
 151                        return res;
 152        }
 153
 154        i2c_set_clientdata(ara, alert);
 155        dev_info(&adapter->dev, "supports SMBALERT#\n");
 156
 157        return 0;
 158}
 159
 160/* IRQ and memory resources are managed so they are freed automatically */
 161static int smbalert_remove(struct i2c_client *ara)
 162{
 163        struct i2c_smbus_alert *alert = i2c_get_clientdata(ara);
 164
 165        cancel_work_sync(&alert->alert);
 166        return 0;
 167}
 168
 169static const struct i2c_device_id smbalert_ids[] = {
 170        { "smbus_alert", 0 },
 171        { /* LIST END */ }
 172};
 173MODULE_DEVICE_TABLE(i2c, smbalert_ids);
 174
 175static struct i2c_driver smbalert_driver = {
 176        .driver = {
 177                .name   = "smbus_alert",
 178        },
 179        .probe          = smbalert_probe,
 180        .remove         = smbalert_remove,
 181        .id_table       = smbalert_ids,
 182};
 183
 184/**
 185 * i2c_handle_smbus_alert - Handle an SMBus alert
 186 * @ara: the ARA client on the relevant adapter
 187 * Context: can't sleep
 188 *
 189 * Helper function to be called from an I2C bus driver's interrupt
 190 * handler. It will schedule the alert work, in turn calling the
 191 * corresponding I2C device driver's alert function.
 192 *
 193 * It is assumed that ara is a valid i2c client previously returned by
 194 * i2c_setup_smbus_alert().
 195 */
 196int i2c_handle_smbus_alert(struct i2c_client *ara)
 197{
 198        struct i2c_smbus_alert *alert = i2c_get_clientdata(ara);
 199
 200        return schedule_work(&alert->alert);
 201}
 202EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert);
 203
 204module_i2c_driver(smbalert_driver);
 205
 206MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
 207MODULE_DESCRIPTION("SMBus protocol extensions support");
 208MODULE_LICENSE("GPL");
 209