18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci#include "loongson_i2c.h"
48c2ecf20Sopenharmony_ci#include "loongson_drv.h"
58c2ecf20Sopenharmony_ci#include "loongson_vbios.h"
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ciu32 ls7a_mm_rreg(struct loongson_drm_device *ldev, u32 offset)
88c2ecf20Sopenharmony_ci{
98c2ecf20Sopenharmony_ci	return readl(ldev->mmio + offset);
108c2ecf20Sopenharmony_ci}
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_civoid ls7a_mm_wreg(struct loongson_drm_device *ldev, u32 offset, u32 val)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	writel(val, ldev->mmio + offset);
158c2ecf20Sopenharmony_ci}
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic inline void __dc_gpio_set_dir(struct loongson_drm_device *ldev,
188c2ecf20Sopenharmony_ci				     unsigned int pin, int input)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	u32 temp;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	temp = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_CFG_OFFSET);
238c2ecf20Sopenharmony_ci	if (input)
248c2ecf20Sopenharmony_ci		temp |= 1UL << pin;
258c2ecf20Sopenharmony_ci	else
268c2ecf20Sopenharmony_ci		temp &= ~(1UL << pin);
278c2ecf20Sopenharmony_ci	ls7a_mm_wreg(ldev, LS7A_DC_GPIO_CFG_OFFSET, temp);
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic void __dc_gpio_set_val(struct loongson_drm_device *ldev, unsigned int pin,
318c2ecf20Sopenharmony_ci			      int high)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	u32 temp;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	temp = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_OUT_OFFSET);
368c2ecf20Sopenharmony_ci	if (high)
378c2ecf20Sopenharmony_ci		temp |= 1UL << pin;
388c2ecf20Sopenharmony_ci	else
398c2ecf20Sopenharmony_ci		temp &= ~(1UL << pin);
408c2ecf20Sopenharmony_ci	ls7a_mm_wreg(ldev, LS7A_DC_GPIO_OUT_OFFSET, temp);
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic void loongson_i2c_set_data(void *i2c, int value)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct loongson_i2c *li2c = i2c;
468c2ecf20Sopenharmony_ci	struct loongson_drm_device *ldev = li2c->ldev;
478c2ecf20Sopenharmony_ci	unsigned int pin = li2c->data;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	if (value)
508c2ecf20Sopenharmony_ci		__dc_gpio_set_dir(ldev, pin, 1);
518c2ecf20Sopenharmony_ci	else {
528c2ecf20Sopenharmony_ci		__dc_gpio_set_val(ldev, pin, 0);
538c2ecf20Sopenharmony_ci		__dc_gpio_set_dir(ldev, pin, 0);
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic void loongson_i2c_set_clock(void *i2c, int value)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct loongson_i2c *li2c = i2c;
608c2ecf20Sopenharmony_ci	struct loongson_drm_device *ldev = li2c->ldev;
618c2ecf20Sopenharmony_ci	unsigned int pin = li2c->clock;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	if (value)
648c2ecf20Sopenharmony_ci		__dc_gpio_set_dir(ldev, pin, 1);
658c2ecf20Sopenharmony_ci	else {
668c2ecf20Sopenharmony_ci		__dc_gpio_set_val(ldev, pin, 0);
678c2ecf20Sopenharmony_ci		__dc_gpio_set_dir(ldev, pin, 0);
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int loongson_i2c_get_data(void *i2c)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	int val;
748c2ecf20Sopenharmony_ci	struct loongson_i2c *li2c = i2c;
758c2ecf20Sopenharmony_ci	struct loongson_drm_device *ldev = li2c->ldev;
768c2ecf20Sopenharmony_ci	unsigned int pin = li2c->data;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	val = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_IN_OFFSET);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	return (val >> pin) & 1;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic int loongson_i2c_get_clock(void *i2c)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	int val;
868c2ecf20Sopenharmony_ci	struct loongson_i2c *li2c = i2c;
878c2ecf20Sopenharmony_ci	struct loongson_drm_device *ldev = li2c->ldev;
888c2ecf20Sopenharmony_ci	unsigned int pin = li2c->clock;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	val = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_IN_OFFSET);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	return (val >> pin) & 1;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic int loongson_i2c_create(struct loongson_drm_device *ldev,
968c2ecf20Sopenharmony_ci			       struct loongson_i2c *li2c, const char *name)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	int ret;
998c2ecf20Sopenharmony_ci	unsigned int i2c_num;
1008c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c_adapter;
1018c2ecf20Sopenharmony_ci	struct i2c_algo_bit_data *i2c_algo_data;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	i2c_num = li2c->i2c_id;
1048c2ecf20Sopenharmony_ci	i2c_adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
1058c2ecf20Sopenharmony_ci	if (!i2c_adapter)
1068c2ecf20Sopenharmony_ci		return -ENOMEM;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	i2c_algo_data = kzalloc(sizeof(struct i2c_algo_bit_data), GFP_KERNEL);
1098c2ecf20Sopenharmony_ci	if (!i2c_algo_data) {
1108c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1118c2ecf20Sopenharmony_ci		goto free_adapter;
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	i2c_adapter->owner = THIS_MODULE;
1158c2ecf20Sopenharmony_ci	i2c_adapter->class = I2C_CLASS_DDC;
1168c2ecf20Sopenharmony_ci	i2c_adapter->algo_data = i2c_algo_data;
1178c2ecf20Sopenharmony_ci	i2c_adapter->dev.parent = ldev->dev->dev;
1188c2ecf20Sopenharmony_ci	i2c_adapter->nr = -1;
1198c2ecf20Sopenharmony_ci	snprintf(i2c_adapter->name, sizeof(i2c_adapter->name), "%s%d", name,
1208c2ecf20Sopenharmony_ci		 i2c_num);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	li2c->data = i2c_num * 2;
1238c2ecf20Sopenharmony_ci	li2c->clock = i2c_num * 2 + 1;
1248c2ecf20Sopenharmony_ci	DRM_INFO("Created dc-i2c%d, sda=%d, scl=%d\n", i2c_num, li2c->data,
1258c2ecf20Sopenharmony_ci		 li2c->clock);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	i2c_algo_data->setsda = loongson_i2c_set_data;
1288c2ecf20Sopenharmony_ci	i2c_algo_data->setscl = loongson_i2c_set_clock;
1298c2ecf20Sopenharmony_ci	i2c_algo_data->getsda = loongson_i2c_get_data;
1308c2ecf20Sopenharmony_ci	i2c_algo_data->getscl = loongson_i2c_get_clock;
1318c2ecf20Sopenharmony_ci	i2c_algo_data->udelay = DC_I2C_TON;
1328c2ecf20Sopenharmony_ci	i2c_algo_data->timeout = usecs_to_jiffies(2200); /* from VESA */
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	ret = i2c_bit_add_numbered_bus(i2c_adapter);
1358c2ecf20Sopenharmony_ci	if (ret)
1368c2ecf20Sopenharmony_ci		goto free_algo_data;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	li2c->adapter = i2c_adapter;
1398c2ecf20Sopenharmony_ci	i2c_algo_data->data = li2c;
1408c2ecf20Sopenharmony_ci	i2c_set_adapdata(li2c->adapter, li2c);
1418c2ecf20Sopenharmony_ci	li2c->init = true;
1428c2ecf20Sopenharmony_ci	li2c->ldev = ldev;
1438c2ecf20Sopenharmony_ci	DRM_INFO("Register i2c algo-bit adapter [%s]\n", i2c_adapter->name);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return 0;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cifree_algo_data:
1488c2ecf20Sopenharmony_ci	DRM_ERROR("Failed to register i2c adapter %s\n", i2c_adapter->name);
1498c2ecf20Sopenharmony_ci	kfree(i2c_algo_data);
1508c2ecf20Sopenharmony_cifree_adapter:
1518c2ecf20Sopenharmony_ci	kfree(i2c_adapter);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return ret;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ciint loongson_gpio_init(struct loongson_drm_device *ldev)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	int pin;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	/* set gpio dir output 0-3 */
1618c2ecf20Sopenharmony_ci	for (pin = 0; pin < 4; pin++) {
1628c2ecf20Sopenharmony_ci		__dc_gpio_set_val(ldev, pin, 0);
1638c2ecf20Sopenharmony_ci		__dc_gpio_set_dir(ldev, pin, 0);
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	return 0;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ciint loongson_i2c_init(struct loongson_drm_device *ldev)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	int ret, i;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	ret = get_loongson_i2c(ldev);
1748c2ecf20Sopenharmony_ci	if (ret != true) {
1758c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to get i2c_id form vbios\n");
1768c2ecf20Sopenharmony_ci		return -ENODEV;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	for (i = 0; i < DC_I2C_BUS_MAX; i++) {
1808c2ecf20Sopenharmony_ci		if (!ldev->i2c_bus[i].use)
1818c2ecf20Sopenharmony_ci			continue;
1828c2ecf20Sopenharmony_ci		ldev->i2c_bus[i].i2c_id = i;
1838c2ecf20Sopenharmony_ci		ret = loongson_i2c_create(ldev, &ldev->i2c_bus[i], DC_I2C_NAME);
1848c2ecf20Sopenharmony_ci		if (ret)
1858c2ecf20Sopenharmony_ci			return ret;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	return 0;
1898c2ecf20Sopenharmony_ci}
190