// SPDX-License-Identifier: GPL-2.0-only // SPDX-FileCopyrightText: Copyright (c) 2023-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. /* * virtual_i2c_mux.c - virtual i2c mux driver for P3762 & P3783 GMSL boards. */ #include #include #include #include #include #include #include #include #define DESER_A (0) #define ENABLE_IMU (0xFE) #define ENABLE_ALL_CC (0xAA) #define DESER_ADDR (0x52) #define DESER_CC_REG (0x0003) extern int max96712_write_reg_Dser(int slaveAddr,int channel, u16 addr, u8 val); static int virtual_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) { int ret = 0; /* Do select 1st channel, to access IMUs from 1st Hawk */ if (!chan) { ret = max96712_write_reg_Dser(DESER_ADDR, DESER_A, DESER_CC_REG, ENABLE_IMU); if (ret) pr_err("%s: Failed to do i2c address trans for IMUs\n",__func__); } return ret; } static int virtual_i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan) { int ret = 0; /* Enable all control channels */ if (!chan) { ret = max96712_write_reg_Dser(DESER_ADDR, DESER_A, DESER_CC_REG, ENABLE_ALL_CC); if (ret) pr_err("%s: Failed to do i2c address trans for IMUs\n",__func__); } return ret; } #if defined(NV_I2C_DRIVER_STRUCT_PROBE_WITHOUT_I2C_DEVICE_ID_ARG) /* Linux 6.3 */ static int virtual_i2c_mux_probe(struct i2c_client *client) #else static int virtual_i2c_mux_probe(struct i2c_client *client, const struct i2c_device_id *id) #endif { struct device *dev = &client->dev; struct device_node *np = dev->of_node; struct device_node *child; struct i2c_mux_core *muxc; struct i2c_adapter *parent; int children; int ret; u32 chan; dev_info(dev, "probing virtual i2c-mux.\n"); if (!np) return -ENODEV; parent = client->adapter; if (IS_ERR(parent)) return dev_err_probe(dev, PTR_ERR(parent), "failed to get i2c parent adapter\n"); children = of_get_child_count(np); dev_info(dev, "No of children = %d\n",children); muxc = i2c_mux_alloc(parent, dev, children, 0, I2C_MUX_LOCKED, virtual_i2c_mux_select, virtual_i2c_mux_deselect); if (!muxc) { ret = -ENOMEM; goto err_parent; } i2c_set_clientdata(client, muxc); for (chan = 0; chan < children; chan++) { pr_info("%s: chan = %d\n",__func__, chan); #if defined(NV_I2C_MUX_ADD_ADAPTER_HAS_NO_CLASS_ARG) ret = i2c_mux_add_adapter(muxc, 0, chan); #else ret = i2c_mux_add_adapter(muxc, 0, chan, 0); #endif if (ret) goto err_children; } dev_info(dev, "Probde is successful!!! \n"); dev_info(dev, "%d-port mux on %s adapter\n", children, parent->name); return 0; err_children: of_node_put(child); i2c_mux_del_adapters(muxc); err_parent: i2c_put_adapter(parent); return ret; } #if defined(NV_I2C_DRIVER_STRUCT_REMOVE_RETURN_TYPE_INT) /* Linux 6.1 */ static int virtual_i2c_mux_remove(struct i2c_client *client) #else static void virtual_i2c_mux_remove(struct i2c_client *client) #endif { struct i2c_mux_core *muxc = i2c_get_clientdata(client); i2c_mux_del_adapters(muxc); i2c_put_adapter(muxc->parent); #if defined(NV_I2C_DRIVER_STRUCT_REMOVE_RETURN_TYPE_INT) /* Linux 6.1 */ return 0; #endif } static const struct of_device_id virtual_i2c_mux_of_match[] = { { .compatible = "nvidia,virtual-i2c-mux", }, {}, }; MODULE_DEVICE_TABLE(of, virtual_i2c_mux_of_match); static const struct i2c_device_id virt_i2c_mux_id[] = { { "virtual-i2c-mux", 0 }, { }, }; MODULE_DEVICE_TABLE(i2c, virt_i2c_mux_id); static struct i2c_driver virtual_i2c_mux_driver = { .probe = virtual_i2c_mux_probe, .remove = virtual_i2c_mux_remove, .id_table = virt_i2c_mux_id, .driver = { .name = "virtual-i2c-mux", .owner = THIS_MODULE, .of_match_table = of_match_ptr(virtual_i2c_mux_of_match), }, }; module_i2c_driver(virtual_i2c_mux_driver); MODULE_DESCRIPTION("Virtual I2C multiplexer driver"); MODULE_AUTHOR("Praveen AC "); MODULE_LICENSE("GPL v2");