root/drivers/hwmon/occ/p8_i2c.c

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

DEFINITIONS

This source file includes following definitions.
  1. p8_i2c_occ_getscom
  2. p8_i2c_occ_putscom
  3. p8_i2c_occ_putscom_u32
  4. p8_i2c_occ_putscom_be
  5. p8_i2c_occ_send_cmd
  6. p8_i2c_occ_probe
  7. p8_i2c_occ_remove

   1 // SPDX-License-Identifier: GPL-2.0+
   2 // Copyright IBM Corp 2019
   3 
   4 #include <linux/device.h>
   5 #include <linux/errno.h>
   6 #include <linux/fsi-occ.h>
   7 #include <linux/i2c.h>
   8 #include <linux/jiffies.h>
   9 #include <linux/module.h>
  10 #include <linux/sched.h>
  11 #include <asm/unaligned.h>
  12 
  13 #include "common.h"
  14 
  15 #define OCC_TIMEOUT_MS                  1000
  16 #define OCC_CMD_IN_PRG_WAIT_MS          50
  17 
  18 /* OCB (on-chip control bridge - interface to OCC) registers */
  19 #define OCB_DATA1                       0x6B035
  20 #define OCB_ADDR                        0x6B070
  21 #define OCB_DATA3                       0x6B075
  22 
  23 /* OCC SRAM address space */
  24 #define OCC_SRAM_ADDR_CMD               0xFFFF6000
  25 #define OCC_SRAM_ADDR_RESP              0xFFFF7000
  26 
  27 #define OCC_DATA_ATTN                   0x20010000
  28 
  29 struct p8_i2c_occ {
  30         struct occ occ;
  31         struct i2c_client *client;
  32 };
  33 
  34 #define to_p8_i2c_occ(x)        container_of((x), struct p8_i2c_occ, occ)
  35 
  36 static int p8_i2c_occ_getscom(struct i2c_client *client, u32 address, u8 *data)
  37 {
  38         ssize_t rc;
  39         __be64 buf;
  40         struct i2c_msg msgs[2];
  41 
  42         /* p8 i2c slave requires shift */
  43         address <<= 1;
  44 
  45         msgs[0].addr = client->addr;
  46         msgs[0].flags = client->flags & I2C_M_TEN;
  47         msgs[0].len = sizeof(u32);
  48         /* address is a scom address; bus-endian */
  49         msgs[0].buf = (char *)&address;
  50 
  51         /* data from OCC is big-endian */
  52         msgs[1].addr = client->addr;
  53         msgs[1].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
  54         msgs[1].len = sizeof(u64);
  55         msgs[1].buf = (char *)&buf;
  56 
  57         rc = i2c_transfer(client->adapter, msgs, 2);
  58         if (rc < 0)
  59                 return rc;
  60 
  61         *(u64 *)data = be64_to_cpu(buf);
  62 
  63         return 0;
  64 }
  65 
  66 static int p8_i2c_occ_putscom(struct i2c_client *client, u32 address, u8 *data)
  67 {
  68         u32 buf[3];
  69         ssize_t rc;
  70 
  71         /* p8 i2c slave requires shift */
  72         address <<= 1;
  73 
  74         /* address is bus-endian; data passed through from user as-is */
  75         buf[0] = address;
  76         memcpy(&buf[1], &data[4], sizeof(u32));
  77         memcpy(&buf[2], data, sizeof(u32));
  78 
  79         rc = i2c_master_send(client, (const char *)buf, sizeof(buf));
  80         if (rc < 0)
  81                 return rc;
  82         else if (rc != sizeof(buf))
  83                 return -EIO;
  84 
  85         return 0;
  86 }
  87 
  88 static int p8_i2c_occ_putscom_u32(struct i2c_client *client, u32 address,
  89                                   u32 data0, u32 data1)
  90 {
  91         u8 buf[8];
  92 
  93         memcpy(buf, &data0, 4);
  94         memcpy(buf + 4, &data1, 4);
  95 
  96         return p8_i2c_occ_putscom(client, address, buf);
  97 }
  98 
  99 static int p8_i2c_occ_putscom_be(struct i2c_client *client, u32 address,
 100                                  u8 *data)
 101 {
 102         __be32 data0, data1;
 103 
 104         memcpy(&data0, data, 4);
 105         memcpy(&data1, data + 4, 4);
 106 
 107         return p8_i2c_occ_putscom_u32(client, address, be32_to_cpu(data0),
 108                                       be32_to_cpu(data1));
 109 }
 110 
 111 static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd)
 112 {
 113         int i, rc;
 114         unsigned long start;
 115         u16 data_length;
 116         const unsigned long timeout = msecs_to_jiffies(OCC_TIMEOUT_MS);
 117         const long wait_time = msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS);
 118         struct p8_i2c_occ *ctx = to_p8_i2c_occ(occ);
 119         struct i2c_client *client = ctx->client;
 120         struct occ_response *resp = &occ->resp;
 121 
 122         start = jiffies;
 123 
 124         /* set sram address for command */
 125         rc = p8_i2c_occ_putscom_u32(client, OCB_ADDR, OCC_SRAM_ADDR_CMD, 0);
 126         if (rc)
 127                 return rc;
 128 
 129         /* write command (expected to already be BE), we need bus-endian... */
 130         rc = p8_i2c_occ_putscom_be(client, OCB_DATA3, cmd);
 131         if (rc)
 132                 return rc;
 133 
 134         /* trigger OCC attention */
 135         rc = p8_i2c_occ_putscom_u32(client, OCB_DATA1, OCC_DATA_ATTN, 0);
 136         if (rc)
 137                 return rc;
 138 
 139         do {
 140                 /* set sram address for response */
 141                 rc = p8_i2c_occ_putscom_u32(client, OCB_ADDR,
 142                                             OCC_SRAM_ADDR_RESP, 0);
 143                 if (rc)
 144                         return rc;
 145 
 146                 rc = p8_i2c_occ_getscom(client, OCB_DATA3, (u8 *)resp);
 147                 if (rc)
 148                         return rc;
 149 
 150                 /* wait for OCC */
 151                 if (resp->return_status == OCC_RESP_CMD_IN_PRG) {
 152                         rc = -EALREADY;
 153 
 154                         if (time_after(jiffies, start + timeout))
 155                                 break;
 156 
 157                         set_current_state(TASK_INTERRUPTIBLE);
 158                         schedule_timeout(wait_time);
 159                 }
 160         } while (rc);
 161 
 162         /* check the OCC response */
 163         switch (resp->return_status) {
 164         case OCC_RESP_CMD_IN_PRG:
 165                 rc = -ETIMEDOUT;
 166                 break;
 167         case OCC_RESP_SUCCESS:
 168                 rc = 0;
 169                 break;
 170         case OCC_RESP_CMD_INVAL:
 171         case OCC_RESP_CMD_LEN_INVAL:
 172         case OCC_RESP_DATA_INVAL:
 173         case OCC_RESP_CHKSUM_ERR:
 174                 rc = -EINVAL;
 175                 break;
 176         case OCC_RESP_INT_ERR:
 177         case OCC_RESP_BAD_STATE:
 178         case OCC_RESP_CRIT_EXCEPT:
 179         case OCC_RESP_CRIT_INIT:
 180         case OCC_RESP_CRIT_WATCHDOG:
 181         case OCC_RESP_CRIT_OCB:
 182         case OCC_RESP_CRIT_HW:
 183                 rc = -EREMOTEIO;
 184                 break;
 185         default:
 186                 rc = -EPROTO;
 187         }
 188 
 189         if (rc < 0)
 190                 return rc;
 191 
 192         data_length = get_unaligned_be16(&resp->data_length);
 193         if (data_length > OCC_RESP_DATA_BYTES)
 194                 return -EMSGSIZE;
 195 
 196         /* fetch the rest of the response data */
 197         for (i = 8; i < data_length + 7; i += 8) {
 198                 rc = p8_i2c_occ_getscom(client, OCB_DATA3, ((u8 *)resp) + i);
 199                 if (rc)
 200                         return rc;
 201         }
 202 
 203         return 0;
 204 }
 205 
 206 static int p8_i2c_occ_probe(struct i2c_client *client,
 207                             const struct i2c_device_id *id)
 208 {
 209         struct occ *occ;
 210         struct p8_i2c_occ *ctx = devm_kzalloc(&client->dev, sizeof(*ctx),
 211                                               GFP_KERNEL);
 212         if (!ctx)
 213                 return -ENOMEM;
 214 
 215         ctx->client = client;
 216         occ = &ctx->occ;
 217         occ->bus_dev = &client->dev;
 218         dev_set_drvdata(&client->dev, occ);
 219 
 220         occ->powr_sample_time_us = 250;
 221         occ->poll_cmd_data = 0x10;              /* P8 OCC poll data */
 222         occ->send_cmd = p8_i2c_occ_send_cmd;
 223 
 224         return occ_setup(occ, "p8_occ");
 225 }
 226 
 227 static int p8_i2c_occ_remove(struct i2c_client *client)
 228 {
 229         struct occ *occ = dev_get_drvdata(&client->dev);
 230 
 231         occ_shutdown(occ);
 232 
 233         return 0;
 234 }
 235 
 236 static const struct of_device_id p8_i2c_occ_of_match[] = {
 237         { .compatible = "ibm,p8-occ-hwmon" },
 238         {}
 239 };
 240 MODULE_DEVICE_TABLE(of, p8_i2c_occ_of_match);
 241 
 242 static struct i2c_driver p8_i2c_occ_driver = {
 243         .class = I2C_CLASS_HWMON,
 244         .driver = {
 245                 .name = "occ-hwmon",
 246                 .of_match_table = p8_i2c_occ_of_match,
 247         },
 248         .probe = p8_i2c_occ_probe,
 249         .remove = p8_i2c_occ_remove,
 250 };
 251 
 252 module_i2c_driver(p8_i2c_occ_driver);
 253 
 254 MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
 255 MODULE_DESCRIPTION("BMC P8 OCC hwmon driver");
 256 MODULE_LICENSE("GPL");

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