1 // SPDX-License-Identifier: GPL-2.0+
2 
3 #include "loongson_i2c.h"
4 #include "loongson_drv.h"
5 #include "loongson_vbios.h"
6 
ls7a_mm_rreg(struct loongson_drm_device *ldev, u32 offset)7 u32 ls7a_mm_rreg(struct loongson_drm_device *ldev, u32 offset)
8 {
9 	return readl(ldev->mmio + offset);
10 }
11 
ls7a_mm_wreg(struct loongson_drm_device *ldev, u32 offset, u32 val)12 void ls7a_mm_wreg(struct loongson_drm_device *ldev, u32 offset, u32 val)
13 {
14 	writel(val, ldev->mmio + offset);
15 }
16 
__dc_gpio_set_dir(struct loongson_drm_device *ldev, unsigned int pin, int input)17 static inline void __dc_gpio_set_dir(struct loongson_drm_device *ldev,
18 				     unsigned int pin, int input)
19 {
20 	u32 temp;
21 
22 	temp = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_CFG_OFFSET);
23 	if (input)
24 		temp |= 1UL << pin;
25 	else
26 		temp &= ~(1UL << pin);
27 	ls7a_mm_wreg(ldev, LS7A_DC_GPIO_CFG_OFFSET, temp);
28 }
29 
__dc_gpio_set_val(struct loongson_drm_device *ldev, unsigned int pin, int high)30 static void __dc_gpio_set_val(struct loongson_drm_device *ldev, unsigned int pin,
31 			      int high)
32 {
33 	u32 temp;
34 
35 	temp = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_OUT_OFFSET);
36 	if (high)
37 		temp |= 1UL << pin;
38 	else
39 		temp &= ~(1UL << pin);
40 	ls7a_mm_wreg(ldev, LS7A_DC_GPIO_OUT_OFFSET, temp);
41 }
42 
loongson_i2c_set_data(void *i2c, int value)43 static void loongson_i2c_set_data(void *i2c, int value)
44 {
45 	struct loongson_i2c *li2c = i2c;
46 	struct loongson_drm_device *ldev = li2c->ldev;
47 	unsigned int pin = li2c->data;
48 
49 	if (value)
50 		__dc_gpio_set_dir(ldev, pin, 1);
51 	else {
52 		__dc_gpio_set_val(ldev, pin, 0);
53 		__dc_gpio_set_dir(ldev, pin, 0);
54 	}
55 }
56 
loongson_i2c_set_clock(void *i2c, int value)57 static void loongson_i2c_set_clock(void *i2c, int value)
58 {
59 	struct loongson_i2c *li2c = i2c;
60 	struct loongson_drm_device *ldev = li2c->ldev;
61 	unsigned int pin = li2c->clock;
62 
63 	if (value)
64 		__dc_gpio_set_dir(ldev, pin, 1);
65 	else {
66 		__dc_gpio_set_val(ldev, pin, 0);
67 		__dc_gpio_set_dir(ldev, pin, 0);
68 	}
69 }
70 
loongson_i2c_get_data(void *i2c)71 static int loongson_i2c_get_data(void *i2c)
72 {
73 	int val;
74 	struct loongson_i2c *li2c = i2c;
75 	struct loongson_drm_device *ldev = li2c->ldev;
76 	unsigned int pin = li2c->data;
77 
78 	val = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_IN_OFFSET);
79 
80 	return (val >> pin) & 1;
81 }
82 
loongson_i2c_get_clock(void *i2c)83 static int loongson_i2c_get_clock(void *i2c)
84 {
85 	int val;
86 	struct loongson_i2c *li2c = i2c;
87 	struct loongson_drm_device *ldev = li2c->ldev;
88 	unsigned int pin = li2c->clock;
89 
90 	val = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_IN_OFFSET);
91 
92 	return (val >> pin) & 1;
93 }
94 
loongson_i2c_create(struct loongson_drm_device *ldev, struct loongson_i2c *li2c, const char *name)95 static int loongson_i2c_create(struct loongson_drm_device *ldev,
96 			       struct loongson_i2c *li2c, const char *name)
97 {
98 	int ret;
99 	unsigned int i2c_num;
100 	struct i2c_adapter *i2c_adapter;
101 	struct i2c_algo_bit_data *i2c_algo_data;
102 
103 	i2c_num = li2c->i2c_id;
104 	i2c_adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
105 	if (!i2c_adapter)
106 		return -ENOMEM;
107 
108 	i2c_algo_data = kzalloc(sizeof(struct i2c_algo_bit_data), GFP_KERNEL);
109 	if (!i2c_algo_data) {
110 		ret = -ENOMEM;
111 		goto free_adapter;
112 	}
113 
114 	i2c_adapter->owner = THIS_MODULE;
115 	i2c_adapter->class = I2C_CLASS_DDC;
116 	i2c_adapter->algo_data = i2c_algo_data;
117 	i2c_adapter->dev.parent = ldev->dev->dev;
118 	i2c_adapter->nr = -1;
119 	snprintf(i2c_adapter->name, sizeof(i2c_adapter->name), "%s%d", name,
120 		 i2c_num);
121 
122 	li2c->data = i2c_num * 2;
123 	li2c->clock = i2c_num * 2 + 1;
124 	DRM_INFO("Created dc-i2c%d, sda=%d, scl=%d\n", i2c_num, li2c->data,
125 		 li2c->clock);
126 
127 	i2c_algo_data->setsda = loongson_i2c_set_data;
128 	i2c_algo_data->setscl = loongson_i2c_set_clock;
129 	i2c_algo_data->getsda = loongson_i2c_get_data;
130 	i2c_algo_data->getscl = loongson_i2c_get_clock;
131 	i2c_algo_data->udelay = DC_I2C_TON;
132 	i2c_algo_data->timeout = usecs_to_jiffies(2200); /* from VESA */
133 
134 	ret = i2c_bit_add_numbered_bus(i2c_adapter);
135 	if (ret)
136 		goto free_algo_data;
137 
138 	li2c->adapter = i2c_adapter;
139 	i2c_algo_data->data = li2c;
140 	i2c_set_adapdata(li2c->adapter, li2c);
141 	li2c->init = true;
142 	li2c->ldev = ldev;
143 	DRM_INFO("Register i2c algo-bit adapter [%s]\n", i2c_adapter->name);
144 
145 	return 0;
146 
147 free_algo_data:
148 	DRM_ERROR("Failed to register i2c adapter %s\n", i2c_adapter->name);
149 	kfree(i2c_algo_data);
150 free_adapter:
151 	kfree(i2c_adapter);
152 
153 	return ret;
154 }
155 
loongson_gpio_init(struct loongson_drm_device *ldev)156 int loongson_gpio_init(struct loongson_drm_device *ldev)
157 {
158 	int pin;
159 
160 	/* set gpio dir output 0-3 */
161 	for (pin = 0; pin < 4; pin++) {
162 		__dc_gpio_set_val(ldev, pin, 0);
163 		__dc_gpio_set_dir(ldev, pin, 0);
164 	}
165 
166 	return 0;
167 }
168 
loongson_i2c_init(struct loongson_drm_device *ldev)169 int loongson_i2c_init(struct loongson_drm_device *ldev)
170 {
171 	int ret, i;
172 
173 	ret = get_loongson_i2c(ldev);
174 	if (ret != true) {
175 		DRM_ERROR("Failed to get i2c_id form vbios\n");
176 		return -ENODEV;
177 	}
178 
179 	for (i = 0; i < DC_I2C_BUS_MAX; i++) {
180 		if (!ldev->i2c_bus[i].use)
181 			continue;
182 		ldev->i2c_bus[i].i2c_id = i;
183 		ret = loongson_i2c_create(ldev, &ldev->i2c_bus[i], DC_I2C_NAME);
184 		if (ret)
185 			return ret;
186 	}
187 
188 	return 0;
189 }
190