// SPDX-License-Identifier: GPL-2.0 /* * Maxim Dual GMSL Deserializer Remode Device Manage * * Copyright (C) 2023 Rockchip Electronics Co., Ltd. * * Author: Cai Wenzhong * */ #include #include #include #include "maxim2c_api.h" static const char *maxim2c_remote_devs_name[MAXIM2C_LINK_ID_MAX] = { "remote0", "remote1" }; static const char *maxim2c_remote_link_compat[MAXIM2C_LINK_ID_MAX] = { "maxim2c,link0", "maxim2c,link1" }; static int maxim2c_remote_dev_info_parse(struct device *dev, struct mfd_cell *remote_mfd_dev, u8 link_id) { struct device_node *node = NULL; const char *remote_device_name = "serdes-remote-device"; const char *prop_str = NULL, *link_compat = NULL; u32 sub_idx = 0, remote_id = 0; int ret = 0; node = NULL; sub_idx = 0; while ((node = of_get_next_child(dev->of_node, node))) { if (!strncasecmp(node->name, remote_device_name, strlen(remote_device_name))) { if (sub_idx >= MAXIM2C_LINK_ID_MAX) { dev_err(dev, "%pOF: Too many matching %s node\n", dev->of_node, remote_device_name); of_node_put(node); break; } if (!of_device_is_available(node)) { dev_info(dev, "%pOF is disabled\n", node); sub_idx++; continue; } /* remote id */ ret = of_property_read_u32(node, "remote-id", &remote_id); if (ret) { sub_idx++; continue; } if (remote_id >= MAXIM2C_LINK_ID_MAX) { sub_idx++; continue; } if (remote_id != link_id) { sub_idx++; continue; } dev_info(dev, "remote device id = %d\n", remote_id); ret = of_property_read_string(node, "compatible", &prop_str); if (ret) { dev_err(dev, "%pOF no compatible error\n", node); of_node_put(node); return -EINVAL; } link_compat = maxim2c_remote_link_compat[remote_id]; if (!strncasecmp(prop_str, link_compat, strlen(link_compat))) { dev_info(dev, "compatible property: %s\n", prop_str); remote_mfd_dev->name = maxim2c_remote_devs_name[remote_id]; remote_mfd_dev->of_compatible = prop_str; of_node_put(node); return 0; } dev_err(dev, "%pOF compatible and remote_id mismatch\n", node); of_node_put(node); return -EINVAL; } } return -EINVAL; } static int maxim2c_remote_mfd_devs_init(maxim2c_t *maxim2c) { struct device *dev = &maxim2c->client->dev; maxim2c_gmsl_link_t *gmsl_link = &maxim2c->gmsl_link; struct mfd_cell *remote_mfd_dev = NULL; int link_idx = 0, nr_mfd_cell = 0; int ret = 0; remote_mfd_dev = maxim2c->remote_mfd_devs; nr_mfd_cell = 0; for (link_idx = 0; link_idx < MAXIM2C_LINK_ID_MAX; link_idx++) { remote_mfd_dev->name = NULL; remote_mfd_dev->of_compatible = NULL; if (gmsl_link->link_cfg[link_idx].link_enable == 0) { dev_dbg(dev, "%s: link id = %d is disabled\n", __func__, link_idx); continue; } ret = maxim2c_remote_dev_info_parse(dev, remote_mfd_dev, link_idx); if (ret == 0) { remote_mfd_dev++; nr_mfd_cell++; } } dev_info(dev, "Total number of remote devices is %d", nr_mfd_cell); return nr_mfd_cell; } int maxim2c_remote_mfd_add_devices(maxim2c_t *maxim2c) { struct device *dev = &maxim2c->client->dev; int nr_mfd_cell = 0, ret = 0; dev_info(dev, "=== maxim2c add remote devices ==="); nr_mfd_cell = maxim2c_remote_mfd_devs_init(maxim2c); if (nr_mfd_cell == 0) { dev_err(dev, "%s: remote mfd devices init error\n", __func__); return -EINVAL; } ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, maxim2c->remote_mfd_devs, nr_mfd_cell, NULL, 0, NULL); if (ret) dev_err(dev, "%s: add remote mfd devices error: %d\n", __func__, ret); return ret; } EXPORT_SYMBOL(maxim2c_remote_mfd_add_devices); int maxim2c_remote_devices_init(maxim2c_t *maxim2c, u8 link_init_mask) { struct device *dev = &maxim2c->client->dev; struct maxim2c_remote *remote_device = NULL; const struct maxim2c_remote_ops *remote_ops = NULL; u8 link_mask = 0, link_enable = 0, link_locked = 0; int ret = 0, i = 0; dev_dbg(dev, "%s: link init mask = 0x%02x\n", __func__, link_init_mask); for (i = 0; i < MAXIM2C_LINK_ID_MAX; i++) { if ((link_init_mask & BIT(i)) == 0) { dev_dbg(dev, "link id = %d init mask is disabled\n", i); continue; } link_enable = maxim2c->gmsl_link.link_cfg[i].link_enable; if (link_enable == 0) { dev_info(dev, "link id = %d is disabled\n", i); continue; } remote_device = maxim2c->remote_device[i]; if (remote_device == NULL) { dev_info(dev, "remote device id = %d isn't detected\n", i); continue; } if (remote_device->remote_enable == 0) { dev_info(dev, "remote device id = %d isn't enabled\n", i); continue; } remote_ops = remote_device->remote_ops; if (remote_ops == NULL) { dev_info(dev, "remote device id = %d is no ops\n", i); continue; } link_mask = BIT(i); link_locked = maxim2c_link_get_lock_state(maxim2c, link_mask); if (link_locked != link_mask) { dev_info(dev, "link id = %d is unlocked\n", i); continue; } maxim2c_link_select_remote_control(maxim2c, link_mask); if (remote_ops->remote_init) ret |= remote_ops->remote_init(remote_device); } return ret; } EXPORT_SYMBOL(maxim2c_remote_devices_init); int maxim2c_remote_devices_deinit(maxim2c_t *maxim2c, u8 link_init_mask) { struct device *dev = &maxim2c->client->dev; struct maxim2c_remote *remote_device = NULL; const struct maxim2c_remote_ops *remote_ops = NULL; u8 link_mask = 0, link_enable = 0, link_locked = 0; int ret = 0, i = 0; dev_dbg(dev, "%s: link init mask = 0x%02x\n", __func__, link_init_mask); for (i = 0; i < MAXIM2C_LINK_ID_MAX; i++) { if ((link_init_mask & BIT(i)) == 0) { dev_dbg(dev, "link id = %d init mask is disabled\n", i); continue; } link_enable = maxim2c->gmsl_link.link_cfg[i].link_enable; if (link_enable == 0) { dev_info(dev, "link id = %d is disabled\n", i); continue; } remote_device = maxim2c->remote_device[i]; if (remote_device == NULL) { dev_info(dev, "remote device id = %d isn't detected\n", i); continue; } if (remote_device->remote_enable == 0) { dev_info(dev, "remote device id = %d isn't enabled\n", i); continue; } remote_ops = remote_device->remote_ops; if (remote_ops == NULL) { dev_info(dev, "remote device id = %d is no ops\n", i); continue; } link_mask = BIT(i); link_locked = maxim2c_link_get_lock_state(maxim2c, link_mask); if (link_locked != link_mask) { dev_info(dev, "link id = %d is unlocked\n", i); continue; } maxim2c_link_select_remote_control(maxim2c, link_mask); if (remote_ops->remote_deinit) ret |= remote_ops->remote_deinit(remote_device); } return ret; } EXPORT_SYMBOL(maxim2c_remote_devices_deinit); int maxim2c_remote_load_init_seq(maxim2c_remote_t *remote_device) { struct device *dev = remote_device->dev; struct device_node *node = NULL; int ret = 0; node = of_get_child_by_name(dev->of_node, "remote-init-sequence"); if (!IS_ERR_OR_NULL(node)) { dev_info(dev, "load remote-init-sequence\n"); ret = maxim2c_i2c_load_init_seq(dev, node, &remote_device->remote_init_seq); of_node_put(node); return ret; } return 0; } EXPORT_SYMBOL(maxim2c_remote_load_init_seq); int maxim2c_remote_i2c_addr_select(maxim2c_remote_t *remote_device, u32 i2c_id) { struct device *dev = remote_device->dev; struct i2c_client *client = remote_device->client; if (i2c_id == MAXIM2C_I2C_SER_DEF) { client->addr = remote_device->ser_i2c_addr_def; dev_info(dev, "ser select default i2c addr = 0x%02x\n", client->addr); } else if (i2c_id == MAXIM2C_I2C_SER_MAP) { client->addr = remote_device->ser_i2c_addr_map; dev_info(dev, "ser select mapping i2c addr = 0x%02x\n", client->addr); } else { dev_err(dev, "i2c select id = %d error\n", i2c_id); return -EINVAL; } return 0; } EXPORT_SYMBOL(maxim2c_remote_i2c_addr_select); int maxim2c_remote_i2c_client_init(maxim2c_remote_t *remote_device, struct i2c_client *des_client) { struct device *dev = remote_device->dev; struct i2c_client *ser_client = NULL; u16 ser_client_addr = 0; if (remote_device->ser_i2c_addr_map) ser_client_addr = remote_device->ser_i2c_addr_map; else ser_client_addr = remote_device->ser_i2c_addr_def; ser_client = devm_i2c_new_dummy_device(&des_client->dev, des_client->adapter, ser_client_addr); if (IS_ERR(ser_client)) { dev_err(dev, "failed to alloc i2c client.\n"); return -PTR_ERR(ser_client); } ser_client->addr = remote_device->ser_i2c_addr_def; remote_device->client = ser_client; i2c_set_clientdata(ser_client, remote_device); dev_info(dev, "remote i2c client init, i2c_addr = 0x%02x\n", ser_client_addr); return 0; } EXPORT_SYMBOL(maxim2c_remote_i2c_client_init); static int maxim2c_remote_device_chain_check(maxim2c_remote_t *remote_device) { struct device *dev = NULL; struct device_node *endpoint = NULL; struct device_node *link_node = NULL; u8 remote_id, link_id; u32 value; int ret = 0; if (remote_device == NULL) { dev_err(dev, "%s: input parameter is error\n", __func__); return -EINVAL; } dev = remote_device->dev; remote_id = remote_device->remote_id; endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); if (!endpoint) { dev_err(dev, "%s: no endpoint error\n", __func__); return -EINVAL; } link_node = of_graph_get_remote_port_parent(endpoint); if (!link_node) { dev_err(dev, "%pOF: endpoint has no remote port parent error\n", endpoint); return -EINVAL; } ret = of_property_read_u32(link_node, "link-id", &value); if (ret) { dev_err(dev, "%pOF: no property link_id error\n", link_node); of_node_put(link_node); return -EINVAL; } of_node_put(link_node); link_id = value; if (remote_id != link_id) { dev_err(dev, "remote_id (%d) != link_id (%d) of %pOF\n", remote_id, link_id, link_node); return -EINVAL; } return 0; } int maxim2c_remote_device_register(maxim2c_t *maxim2c, maxim2c_remote_t *remote_device) { struct device *dev = NULL; u8 remote_id; int ret = 0; if ((maxim2c == NULL) || (remote_device == NULL)) { dev_err(dev, "%s: input parameter is error!\n", __func__); return -EINVAL; } dev = remote_device->dev; remote_id = remote_device->remote_id; if (remote_id >= MAXIM2C_LINK_ID_MAX) { dev_err(dev, "%s: remote_id = %d is error\n", __func__, remote_id); return -EINVAL; } if (maxim2c->remote_device[remote_id] != NULL) { dev_err(dev, "%s: remote_id = %d is conflict\n", __func__, remote_id); return -EINVAL; } ret = maxim2c_remote_device_chain_check(remote_device); if (ret) { dev_err(dev, "%s: remote device id = %d chain error\n", __func__, remote_id); return -EINVAL; } remote_device->remote_enable = 1; maxim2c->remote_device[remote_id] = remote_device; return 0; } EXPORT_SYMBOL(maxim2c_remote_device_register);