root/drivers/i2c/busses/i2c-opal.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. i2c_opal_translate_error
  2. i2c_opal_send_request
  3. i2c_opal_master_xfer
  4. i2c_opal_smbus_xfer
  5. i2c_opal_func
  6. i2c_opal_probe
  7. i2c_opal_remove
  8. i2c_opal_init
  9. i2c_opal_exit

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * IBM OPAL I2C driver
   4  * Copyright (C) 2014 IBM
   5  */
   6 
   7 #include <linux/device.h>
   8 #include <linux/i2c.h>
   9 #include <linux/kernel.h>
  10 #include <linux/mm.h>
  11 #include <linux/module.h>
  12 #include <linux/of.h>
  13 #include <linux/platform_device.h>
  14 #include <linux/slab.h>
  15 
  16 #include <asm/firmware.h>
  17 #include <asm/opal.h>
  18 
  19 static int i2c_opal_translate_error(int rc)
  20 {
  21         switch (rc) {
  22         case OPAL_NO_MEM:
  23                 return -ENOMEM;
  24         case OPAL_PARAMETER:
  25                 return -EINVAL;
  26         case OPAL_I2C_ARBT_LOST:
  27                 return -EAGAIN;
  28         case OPAL_I2C_TIMEOUT:
  29                 return -ETIMEDOUT;
  30         case OPAL_I2C_NACK_RCVD:
  31                 return -ENXIO;
  32         case OPAL_I2C_STOP_ERR:
  33                 return -EBUSY;
  34         default:
  35                 return -EIO;
  36         }
  37 }
  38 
  39 static int i2c_opal_send_request(u32 bus_id, struct opal_i2c_request *req)
  40 {
  41         struct opal_msg msg;
  42         int token, rc;
  43 
  44         token = opal_async_get_token_interruptible();
  45         if (token < 0) {
  46                 if (token != -ERESTARTSYS)
  47                         pr_err("Failed to get the async token\n");
  48 
  49                 return token;
  50         }
  51 
  52         rc = opal_i2c_request(token, bus_id, req);
  53         if (rc != OPAL_ASYNC_COMPLETION) {
  54                 rc = i2c_opal_translate_error(rc);
  55                 goto exit;
  56         }
  57 
  58         rc = opal_async_wait_response(token, &msg);
  59         if (rc)
  60                 goto exit;
  61 
  62         rc = opal_get_async_rc(msg);
  63         if (rc != OPAL_SUCCESS) {
  64                 rc = i2c_opal_translate_error(rc);
  65                 goto exit;
  66         }
  67 
  68 exit:
  69         opal_async_release_token(token);
  70         return rc;
  71 }
  72 
  73 static int i2c_opal_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
  74                                 int num)
  75 {
  76         unsigned long opal_id = (unsigned long)adap->algo_data;
  77         struct opal_i2c_request req;
  78         int rc, i;
  79 
  80         /* We only support fairly simple combinations here of one
  81          * or two messages
  82          */
  83         memset(&req, 0, sizeof(req));
  84         switch(num) {
  85         case 1:
  86                 req.type = (msgs[0].flags & I2C_M_RD) ?
  87                         OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE;
  88                 req.addr = cpu_to_be16(msgs[0].addr);
  89                 req.size = cpu_to_be32(msgs[0].len);
  90                 req.buffer_ra = cpu_to_be64(__pa(msgs[0].buf));
  91                 break;
  92         case 2:
  93                 req.type = (msgs[1].flags & I2C_M_RD) ?
  94                         OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE;
  95                 req.addr = cpu_to_be16(msgs[0].addr);
  96                 req.subaddr_sz = msgs[0].len;
  97                 for (i = 0; i < msgs[0].len; i++)
  98                         req.subaddr = (req.subaddr << 8) | msgs[0].buf[i];
  99                 req.subaddr = cpu_to_be32(req.subaddr);
 100                 req.size = cpu_to_be32(msgs[1].len);
 101                 req.buffer_ra = cpu_to_be64(__pa(msgs[1].buf));
 102                 break;
 103         }
 104 
 105         rc = i2c_opal_send_request(opal_id, &req);
 106         if (rc)
 107                 return rc;
 108 
 109         return num;
 110 }
 111 
 112 static int i2c_opal_smbus_xfer(struct i2c_adapter *adap, u16 addr,
 113                                unsigned short flags, char read_write,
 114                                u8 command, int size, union i2c_smbus_data *data)
 115 {
 116         unsigned long opal_id = (unsigned long)adap->algo_data;
 117         struct opal_i2c_request req;
 118         u8 local[2];
 119         int rc;
 120 
 121         memset(&req, 0, sizeof(req));
 122 
 123         req.addr = cpu_to_be16(addr);
 124         switch (size) {
 125         case I2C_SMBUS_BYTE:
 126                 req.buffer_ra = cpu_to_be64(__pa(&data->byte));
 127                 req.size = cpu_to_be32(1);
 128                 /* Fall through */
 129         case I2C_SMBUS_QUICK:
 130                 req.type = (read_write == I2C_SMBUS_READ) ?
 131                         OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE;
 132                 break;
 133         case I2C_SMBUS_BYTE_DATA:
 134                 req.buffer_ra = cpu_to_be64(__pa(&data->byte));
 135                 req.size = cpu_to_be32(1);
 136                 req.subaddr = cpu_to_be32(command);
 137                 req.subaddr_sz = 1;
 138                 req.type = (read_write == I2C_SMBUS_READ) ?
 139                         OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE;
 140                 break;
 141         case I2C_SMBUS_WORD_DATA:
 142                 if (!read_write) {
 143                         local[0] = data->word & 0xff;
 144                         local[1] = (data->word >> 8) & 0xff;
 145                 }
 146                 req.buffer_ra = cpu_to_be64(__pa(local));
 147                 req.size = cpu_to_be32(2);
 148                 req.subaddr = cpu_to_be32(command);
 149                 req.subaddr_sz = 1;
 150                 req.type = (read_write == I2C_SMBUS_READ) ?
 151                         OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE;
 152                 break;
 153         case I2C_SMBUS_I2C_BLOCK_DATA:
 154                 req.buffer_ra = cpu_to_be64(__pa(&data->block[1]));
 155                 req.size = cpu_to_be32(data->block[0]);
 156                 req.subaddr = cpu_to_be32(command);
 157                 req.subaddr_sz = 1;
 158                 req.type = (read_write == I2C_SMBUS_READ) ?
 159                         OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE;
 160                 break;
 161         default:
 162                 return -EINVAL;
 163         }
 164 
 165         rc = i2c_opal_send_request(opal_id, &req);
 166         if (!rc && read_write && size == I2C_SMBUS_WORD_DATA) {
 167                 data->word = ((u16)local[1]) << 8;
 168                 data->word |= local[0];
 169         }
 170 
 171         return rc;
 172 }
 173 
 174 static u32 i2c_opal_func(struct i2c_adapter *adapter)
 175 {
 176         return I2C_FUNC_I2C | I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
 177                I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
 178                I2C_FUNC_SMBUS_I2C_BLOCK;
 179 }
 180 
 181 static const struct i2c_algorithm i2c_opal_algo = {
 182         .master_xfer    = i2c_opal_master_xfer,
 183         .smbus_xfer     = i2c_opal_smbus_xfer,
 184         .functionality  = i2c_opal_func,
 185 };
 186 
 187 /*
 188  * For two messages, we basically support simple smbus transactions of a
 189  * write-then-anything.
 190  */
 191 static const struct i2c_adapter_quirks i2c_opal_quirks = {
 192         .flags = I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | I2C_AQ_COMB_SAME_ADDR,
 193         .max_comb_1st_msg_len = 4,
 194 };
 195 
 196 static int i2c_opal_probe(struct platform_device *pdev)
 197 {
 198         struct i2c_adapter      *adapter;
 199         const char              *pname;
 200         u32                     opal_id;
 201         int                     rc;
 202 
 203         if (!pdev->dev.of_node)
 204                 return -ENODEV;
 205 
 206         rc = of_property_read_u32(pdev->dev.of_node, "ibm,opal-id", &opal_id);
 207         if (rc) {
 208                 dev_err(&pdev->dev, "Missing ibm,opal-id property !\n");
 209                 return -EIO;
 210         }
 211 
 212         adapter = devm_kzalloc(&pdev->dev, sizeof(*adapter), GFP_KERNEL);
 213         if (!adapter)
 214                 return -ENOMEM;
 215 
 216         adapter->algo = &i2c_opal_algo;
 217         adapter->algo_data = (void *)(unsigned long)opal_id;
 218         adapter->quirks = &i2c_opal_quirks;
 219         adapter->dev.parent = &pdev->dev;
 220         adapter->dev.of_node = of_node_get(pdev->dev.of_node);
 221         pname = of_get_property(pdev->dev.of_node, "ibm,port-name", NULL);
 222         if (pname)
 223                 strlcpy(adapter->name, pname, sizeof(adapter->name));
 224         else
 225                 strlcpy(adapter->name, "opal", sizeof(adapter->name));
 226 
 227         platform_set_drvdata(pdev, adapter);
 228         rc = i2c_add_adapter(adapter);
 229         if (rc)
 230                 dev_err(&pdev->dev, "Failed to register the i2c adapter\n");
 231 
 232         return rc;
 233 }
 234 
 235 static int i2c_opal_remove(struct platform_device *pdev)
 236 {
 237         struct i2c_adapter *adapter = platform_get_drvdata(pdev);
 238 
 239         i2c_del_adapter(adapter);
 240 
 241         return 0;
 242 }
 243 
 244 static const struct of_device_id i2c_opal_of_match[] = {
 245         {
 246                 .compatible = "ibm,opal-i2c",
 247         },
 248         { }
 249 };
 250 MODULE_DEVICE_TABLE(of, i2c_opal_of_match);
 251 
 252 static struct platform_driver i2c_opal_driver = {
 253         .probe  = i2c_opal_probe,
 254         .remove = i2c_opal_remove,
 255         .driver = {
 256                 .name           = "i2c-opal",
 257                 .of_match_table = i2c_opal_of_match,
 258         },
 259 };
 260 
 261 static int __init i2c_opal_init(void)
 262 {
 263         if (!firmware_has_feature(FW_FEATURE_OPAL))
 264                 return -ENODEV;
 265 
 266         return platform_driver_register(&i2c_opal_driver);
 267 }
 268 module_init(i2c_opal_init);
 269 
 270 static void __exit i2c_opal_exit(void)
 271 {
 272         return platform_driver_unregister(&i2c_opal_driver);
 273 }
 274 module_exit(i2c_opal_exit);
 275 
 276 MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>");
 277 MODULE_DESCRIPTION("IBM OPAL I2C driver");
 278 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */