mirror of
https://github.com/corundum/corundum.git
synced 2025-01-16 08:12:53 +08:00
448fa8eb4c
Signed-off-by: Alex Forencich <alex@alexforencich.com>
157 lines
3.4 KiB
C
157 lines
3.4 KiB
C
// SPDX-License-Identifier: BSD-2-Clause-Views
|
|
/*
|
|
* Copyright (c) 2019-2023 The Regents of the University of California
|
|
*/
|
|
|
|
#include "mqnic.h"
|
|
|
|
static void mqnic_i2c_set_scl(void *data, int state)
|
|
{
|
|
struct mqnic_i2c_bus *bus = data;
|
|
|
|
if (state)
|
|
iowrite32(ioread32(bus->scl_out_reg) | bus->scl_out_mask, bus->scl_out_reg);
|
|
else
|
|
iowrite32(ioread32(bus->scl_out_reg) & ~bus->scl_out_mask, bus->scl_out_reg);
|
|
}
|
|
|
|
static void mqnic_i2c_set_sda(void *data, int state)
|
|
{
|
|
struct mqnic_i2c_bus *bus = data;
|
|
|
|
if (state)
|
|
iowrite32(ioread32(bus->sda_out_reg) | bus->sda_out_mask, bus->sda_out_reg);
|
|
else
|
|
iowrite32(ioread32(bus->sda_out_reg) & ~bus->sda_out_mask, bus->sda_out_reg);
|
|
}
|
|
|
|
static int mqnic_i2c_get_scl(void *data)
|
|
{
|
|
struct mqnic_i2c_bus *bus = data;
|
|
|
|
return !!(ioread32(bus->scl_in_reg) & bus->scl_in_mask);
|
|
}
|
|
|
|
static int mqnic_i2c_get_sda(void *data)
|
|
{
|
|
struct mqnic_i2c_bus *bus = data;
|
|
|
|
return !!(ioread32(bus->sda_in_reg) & bus->sda_in_mask);
|
|
}
|
|
|
|
struct mqnic_i2c_bus *mqnic_i2c_bus_create(struct mqnic_dev *mqnic, int index)
|
|
{
|
|
struct mqnic_i2c_bus *bus;
|
|
struct i2c_algo_bit_data *algo;
|
|
struct i2c_adapter *adapter;
|
|
struct mqnic_reg_block *rb;
|
|
|
|
rb = mqnic_find_reg_block(mqnic->rb_list, MQNIC_RB_I2C_TYPE, MQNIC_RB_I2C_VER, index);
|
|
|
|
if (!rb)
|
|
return NULL;
|
|
|
|
bus = kzalloc(sizeof(*bus), GFP_KERNEL);
|
|
|
|
if (!bus)
|
|
return NULL;
|
|
|
|
// set private data
|
|
bus->mqnic = mqnic;
|
|
bus->scl_in_reg = rb->regs + MQNIC_RB_I2C_REG_CTRL;
|
|
bus->scl_out_reg = rb->regs + MQNIC_RB_I2C_REG_CTRL;
|
|
bus->sda_in_reg = rb->regs + MQNIC_RB_I2C_REG_CTRL;
|
|
bus->sda_out_reg = rb->regs + MQNIC_RB_I2C_REG_CTRL;
|
|
bus->scl_in_mask = MQNIC_REG_GPIO_I2C_SCL_IN;
|
|
bus->scl_out_mask = MQNIC_REG_GPIO_I2C_SCL_OUT;
|
|
bus->sda_in_mask = MQNIC_REG_GPIO_I2C_SDA_IN;
|
|
bus->sda_out_mask = MQNIC_REG_GPIO_I2C_SDA_OUT;
|
|
|
|
// bit-bang algorithm setup
|
|
algo = &bus->algo;
|
|
algo->udelay = 5;
|
|
algo->timeout = usecs_to_jiffies(2000);
|
|
algo->setsda = mqnic_i2c_set_sda;
|
|
algo->setscl = mqnic_i2c_set_scl;
|
|
algo->getsda = mqnic_i2c_get_sda;
|
|
algo->getscl = mqnic_i2c_get_scl;
|
|
algo->data = bus;
|
|
|
|
// adapter setup
|
|
adapter = &bus->adapter;
|
|
adapter->owner = THIS_MODULE;
|
|
adapter->algo_data = algo;
|
|
adapter->dev.parent = mqnic->dev;
|
|
snprintf(adapter->name, sizeof(adapter->name), "%s I2C%d", mqnic->name,
|
|
mqnic->i2c_adapter_count);
|
|
|
|
if (i2c_bit_add_bus(adapter)) {
|
|
dev_err(mqnic->dev, "Failed to register I2C adapter");
|
|
goto err_free_bus;
|
|
}
|
|
|
|
list_add_tail(&bus->head, &mqnic->i2c_bus);
|
|
|
|
mqnic->i2c_adapter_count++;
|
|
|
|
return bus;
|
|
|
|
err_free_bus:
|
|
kfree(bus);
|
|
return NULL;
|
|
}
|
|
|
|
struct i2c_adapter *mqnic_i2c_adapter_create(struct mqnic_dev *mqnic, int index)
|
|
{
|
|
struct mqnic_i2c_bus *bus = mqnic_i2c_bus_create(mqnic, index);
|
|
|
|
if (!bus)
|
|
return NULL;
|
|
|
|
return &bus->adapter;
|
|
}
|
|
|
|
void mqnic_i2c_bus_release(struct mqnic_i2c_bus *bus)
|
|
{
|
|
struct mqnic_dev *mqnic;
|
|
|
|
if (!bus)
|
|
return;
|
|
|
|
mqnic = bus->mqnic;
|
|
|
|
mqnic->i2c_adapter_count--;
|
|
|
|
i2c_del_adapter(&bus->adapter);
|
|
list_del(&bus->head);
|
|
kfree(bus);
|
|
}
|
|
|
|
void mqnic_i2c_adapter_release(struct i2c_adapter *adapter)
|
|
{
|
|
struct mqnic_i2c_bus *bus;
|
|
|
|
if (!adapter)
|
|
return;
|
|
|
|
bus = container_of(adapter, struct mqnic_i2c_bus, adapter);
|
|
mqnic_i2c_bus_release(bus);
|
|
}
|
|
|
|
int mqnic_i2c_init(struct mqnic_dev *mqnic)
|
|
{
|
|
INIT_LIST_HEAD(&mqnic->i2c_bus);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void mqnic_i2c_deinit(struct mqnic_dev *mqnic)
|
|
{
|
|
struct mqnic_i2c_bus *bus;
|
|
|
|
while (!list_empty(&mqnic->i2c_bus)) {
|
|
bus = list_first_entry(&mqnic->i2c_bus, typeof(*bus), head);
|
|
mqnic_i2c_bus_release(bus);
|
|
}
|
|
}
|