cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

scd30_i2c.c (3288B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Sensirion SCD30 carbon dioxide sensor i2c driver
      4 *
      5 * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com>
      6 *
      7 * I2C slave address: 0x61
      8 */
      9#include <linux/crc8.h>
     10#include <linux/device.h>
     11#include <linux/errno.h>
     12#include <linux/i2c.h>
     13#include <linux/mod_devicetable.h>
     14#include <linux/module.h>
     15#include <linux/types.h>
     16#include <asm/unaligned.h>
     17
     18#include "scd30.h"
     19
     20#define SCD30_I2C_MAX_BUF_SIZE 18
     21#define SCD30_I2C_CRC8_POLYNOMIAL 0x31
     22
     23static u16 scd30_i2c_cmd_lookup_tbl[] = {
     24	[CMD_START_MEAS] = 0x0010,
     25	[CMD_STOP_MEAS] = 0x0104,
     26	[CMD_MEAS_INTERVAL] = 0x4600,
     27	[CMD_MEAS_READY] = 0x0202,
     28	[CMD_READ_MEAS] = 0x0300,
     29	[CMD_ASC] = 0x5306,
     30	[CMD_FRC] = 0x5204,
     31	[CMD_TEMP_OFFSET] = 0x5403,
     32	[CMD_FW_VERSION] = 0xd100,
     33	[CMD_RESET] = 0xd304,
     34};
     35
     36DECLARE_CRC8_TABLE(scd30_i2c_crc8_tbl);
     37
     38static int scd30_i2c_xfer(struct scd30_state *state, char *txbuf, int txsize,
     39			  char *rxbuf, int rxsize)
     40{
     41	struct i2c_client *client = to_i2c_client(state->dev);
     42	int ret;
     43
     44	/*
     45	 * repeated start is not supported hence instead of sending two i2c
     46	 * messages in a row we send one by one
     47	 */
     48	ret = i2c_master_send(client, txbuf, txsize);
     49	if (ret < 0)
     50		return ret;
     51	if (ret != txsize)
     52		return -EIO;
     53
     54	if (!rxbuf)
     55		return 0;
     56
     57	ret = i2c_master_recv(client, rxbuf, rxsize);
     58	if (ret < 0)
     59		return ret;
     60	if (ret != rxsize)
     61		return -EIO;
     62
     63	return 0;
     64}
     65
     66static int scd30_i2c_command(struct scd30_state *state, enum scd30_cmd cmd, u16 arg,
     67			     void *response, int size)
     68{
     69	char buf[SCD30_I2C_MAX_BUF_SIZE];
     70	char *rsp = response;
     71	int i, ret;
     72	char crc;
     73
     74	put_unaligned_be16(scd30_i2c_cmd_lookup_tbl[cmd], buf);
     75	i = 2;
     76
     77	if (rsp) {
     78		/* each two bytes are followed by a crc8 */
     79		size += size / 2;
     80	} else {
     81		put_unaligned_be16(arg, buf + i);
     82		crc = crc8(scd30_i2c_crc8_tbl, buf + i, 2, CRC8_INIT_VALUE);
     83		i += 2;
     84		buf[i] = crc;
     85		i += 1;
     86
     87		/* commands below don't take an argument */
     88		if ((cmd == CMD_STOP_MEAS) || (cmd == CMD_RESET))
     89			i -= 3;
     90	}
     91
     92	ret = scd30_i2c_xfer(state, buf, i, buf, size);
     93	if (ret)
     94		return ret;
     95
     96	/* validate received data and strip off crc bytes */
     97	for (i = 0; i < size; i += 3) {
     98		crc = crc8(scd30_i2c_crc8_tbl, buf + i, 2, CRC8_INIT_VALUE);
     99		if (crc != buf[i + 2]) {
    100			dev_err(state->dev, "data integrity check failed\n");
    101			return -EIO;
    102		}
    103
    104		*rsp++ = buf[i];
    105		*rsp++ = buf[i + 1];
    106	}
    107
    108	return 0;
    109}
    110
    111static int scd30_i2c_probe(struct i2c_client *client)
    112{
    113	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
    114		return -EOPNOTSUPP;
    115
    116	crc8_populate_msb(scd30_i2c_crc8_tbl, SCD30_I2C_CRC8_POLYNOMIAL);
    117
    118	return scd30_probe(&client->dev, client->irq, client->name, NULL, scd30_i2c_command);
    119}
    120
    121static const struct of_device_id scd30_i2c_of_match[] = {
    122	{ .compatible = "sensirion,scd30" },
    123	{ }
    124};
    125MODULE_DEVICE_TABLE(of, scd30_i2c_of_match);
    126
    127static struct i2c_driver scd30_i2c_driver = {
    128	.driver = {
    129		.name = KBUILD_MODNAME,
    130		.of_match_table = scd30_i2c_of_match,
    131		.pm = pm_sleep_ptr(&scd30_pm_ops),
    132	},
    133	.probe_new = scd30_i2c_probe,
    134};
    135module_i2c_driver(scd30_i2c_driver);
    136
    137MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>");
    138MODULE_DESCRIPTION("Sensirion SCD30 carbon dioxide sensor i2c driver");
    139MODULE_LICENSE("GPL v2");
    140MODULE_IMPORT_NS(IIO_SCD30);