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
  70        ara = alert->ara;
  71
  72        for (;;) {
  73                s32 status;
  74                struct alert_data data;
  75
  76                /*
  77                 * Devices with pending alerts reply in address order, low
  78                 * to high, because of slave transmit arbitration.  After
  79                 * responding, an SMBus device stops asserting SMBALERT#.
  80                 *
  81                 * Note that SMBus 2.0 reserves 10-bit addresses for future
  82                 * use.  We neither handle them, nor try to use PEC here.
  83                 */
  84                status = i2c_smbus_read_byte(ara);
  85                if (status < 0)
  86                        break;
  87
  88                data.data = status & 1;
  89                data.addr = status >> 1;
  90                data.type = I2C_PROTOCOL_SMBUS_ALERT;
  91
  92                dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n",
  93                        data.addr, data.data);
  94
  95                /* Notify driver for the device which issued the alert */
  96                device_for_each_child(&ara->adapter->dev, &data,
  97                                      smbus_do_alert);
  98        }
  99
 100        return IRQ_HANDLED;
 101}
 102
 103static void smbalert_work(struct work_struct *work)
 104{
 105        struct i2c_smbus_alert *alert;
 106
 107        alert = container_of(work, struct i2c_smbus_alert, alert);
 108
 109        smbus_alert(0, alert);
 110
 111}
 112
 113/* Setup SMBALERT# infrastructure */
 114static int smbalert_probe(struct i2c_client *ara,
 115                          const struct i2c_device_id *id)
 116{
 117        struct i2c_smbus_alert_setup *setup = dev_get_platdata(&ara->dev);
 118        struct i2c_smbus_alert *alert;
 119        struct i2c_adapter *adapter = ara->adapter;
 120        int res, irq;
 121
 122        alert = devm_kzalloc(&ara->dev, sizeof(struct i2c_smbus_alert),
 123                             GFP_KERNEL);
 124        if (!alert)
 125                return -ENOMEM;
 126
 127        if (setup) {
 128                irq = setup->irq;
 129        } else {
 130                irq = of_irq_get_byname(adapter->dev.of_node, "smbus_alert");
 131                if (irq <= 0)
 132                        return irq;
 133        }
 134
 135        INIT_WORK(&alert->alert, smbalert_work);
 136        alert->ara = ara;
 137
 138        if (irq > 0) {
 139                res = devm_request_threaded_irq(&ara->dev, irq,
 140                                                NULL, smbus_alert,
 141                                                IRQF_SHARED | IRQF_ONESHOT,
 142                                                "smbus_alert", alert);
 143                if (res)
 144                        return res;
 145        }
 146
 147        i2c_set_clientdata(ara, alert);
 148        dev_info(&adapter->dev, "supports SMBALERT#\n");
 149
 150        return 0;
 151}
 152
 153/* IRQ and memory resources are managed so they are freed automatically */
 154static int smbalert_remove(struct i2c_client *ara)
 155{
 156        struct i2c_smbus_alert *alert = i2c_get_clientdata(ara);
 157
 158        cancel_work_sync(&alert->alert);
 159        return 0;
 160}
 161
 162static const struct i2c_device_id smbalert_ids[] = {
 163        { "smbus_alert", 0 },
 164        { /* LIST END */ }
 165};
 166MODULE_DEVICE_TABLE(i2c, smbalert_ids);
 167
 168static struct i2c_driver smbalert_driver = {
 169        .driver = {
 170                .name   = "smbus_alert",
 171        },
 172        .probe          = smbalert_probe,
 173        .remove         = smbalert_remove,
 174        .id_table       = smbalert_ids,
 175};
 176
 177/**
 178 * i2c_handle_smbus_alert - Handle an SMBus alert
 179 * @ara: the ARA client on the relevant adapter
 180 * Context: can't sleep
 181 *
 182 * Helper function to be called from an I2C bus driver's interrupt
 183 * handler. It will schedule the alert work, in turn calling the
 184 * corresponding I2C device driver's alert function.
 185 *
 186 * It is assumed that ara is a valid i2c client previously returned by
 187 * i2c_setup_smbus_alert().
 188 */
 189int i2c_handle_smbus_alert(struct i2c_client *ara)
 190{
 191        struct i2c_smbus_alert *alert = i2c_get_clientdata(ara);
 192
 193        return schedule_work(&alert->alert);
 194}
 195EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert);
 196
 197module_i2c_driver(smbalert_driver);
 198
 199MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
 200MODULE_DESCRIPTION("SMBus protocol extensions support");
 201MODULE_LICENSE("GPL");
 202