summaryrefslogtreecommitdiff
path: root/bsp/raspberry-pi/dm/mfd/rpi-sense.c
blob: 236f0cd2e12190eead51cfc6a55c81fce927df54 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2023-02-25     GuEe-GUI     the first version
 */

#include <rtthread.h>
#include <rtdevice.h>

#define DBG_TAG "mfd.rpi-sense"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>

#include "rpi-sense.h"

static rt_int32_t i2c_smbus_read_byte_data(struct rt_i2c_client *client,
        rt_uint8_t command)
{
    rt_int32_t res;
    rt_uint8_t ret = 0;
    struct rt_i2c_msg msg[2];

    msg[0].buf = &command;
    msg[0].addr = client->client_addr;
    msg[0].len = 1;
    msg[0].flags = RT_I2C_WR;

    msg[1].buf = &ret;
    msg[1].addr = client->client_addr;
    msg[1].len = 1;
    msg[1].flags = RT_I2C_RD;

    res = rt_i2c_transfer(client->bus, msg, 2);

    return res == 2 ? ret : res;
}

rt_int32_t rpisense_reg_read(struct rpisense *rpisense, int reg)
{
    rt_int32_t ret = i2c_smbus_read_byte_data(rpisense->i2c_client, reg);

    if (ret < 0)
    {
        LOG_E("Read from reg(%d) error = %s", reg, rt_strerror(ret));
    }

    /*
     * As the BCM270x I2C clock stretching bug, some values may have MSB set.
     * Clear it to avoid incorrect values.
     */
    return ret & 0x7f;
}

rt_ssize_t rpisense_block_write(struct rpisense *rpisense, const char *buf, int count)
{
    rt_ssize_t ret;
    struct rt_i2c_client *client = rpisense->i2c_client;

    ret = rt_i2c_master_send(client->bus, client->client_addr, 0, (void *)buf, count);

    if (ret < 0)
    {
        LOG_E("Write block(%d) error = %s", count, rt_strerror(ret));
    }

    return ret;
}

static void rpisense_client_dev_register(struct rpisense *rpisense,
        const char *name, struct rt_platform_device *pdev)
{
    pdev->name = name;
    pdev->priv = rpisense;

    rt_platform_device_register(pdev);
}

static rt_err_t rpisense_probe(struct rt_i2c_client *client)
{
    rt_err_t err;
    rt_int32_t value;
    struct rt_device *dev = &client->parent;
    struct rpisense *rpisense = rt_calloc(1, sizeof(*rpisense));

    if (!rpisense)
    {
        return -RT_ENOMEM;
    }

    rpisense->i2c_client = client;

    value = rpisense_reg_read(rpisense, RPISENSE_WAI);
    if (value > 0)
    {
        if (value != RPISENSE_ID)
        {
            err = -RT_EINVAL;
            goto _fail;
        }
    }
    else
    {
        err = value;
        goto _fail;
    }

    value = rpisense_reg_read(rpisense, RPISENSE_VER);
    if (value < 0)
    {
        err = value;
        goto _fail;
    }

    LOG_I("Raspberry Pi Sense HAT firmware version %i", value);

    dev->user_data = rpisense;

    rpisense_client_dev_register(rpisense, "rpi-sense-js", &rpisense->joystick_dev);
    rpisense_client_dev_register(rpisense, "rpi-sense-fb", &rpisense->framebuffer_dev);

    return RT_EOK;

_fail:
    rt_free(rpisense);

    return err;
}

static rt_err_t rpisense_remove(struct rt_i2c_client *client)
{
    struct rpisense *rpisense = client->parent.user_data;

    rt_bus_remove_device(&rpisense->joystick_dev.parent);
    rt_bus_remove_device(&rpisense->framebuffer_dev.parent);

    rt_free(rpisense);

    return RT_EOK;
}

static const struct rt_i2c_device_id rpisense_ids[] =
{
    { .name = "rpi-sense" },
    { /* sentinel */ },
};

static const struct rt_ofw_node_id rpisense_ofw_ids[] =
{
    { .compatible = "rpi,rpi-sense" },
    { /* sentinel */ },
};

static struct rt_i2c_driver rpisense_driver =
{
    .ids = rpisense_ids,
    .ofw_ids = rpisense_ofw_ids,

    .probe = rpisense_probe,
    .remove = rpisense_remove,
};
RT_I2C_DRIVER_EXPORT(rpisense_driver);