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