1// SPDX-License-Identifier: GPL-2.0+ 2 3#include "loongson_i2c.h" 4#include "loongson_drv.h" 5#include "loongson_vbios.h" 6 7u32 ls7a_mm_rreg(struct loongson_drm_device *ldev, u32 offset) 8{ 9 return readl(ldev->mmio + offset); 10} 11 12void ls7a_mm_wreg(struct loongson_drm_device *ldev, u32 offset, u32 val) 13{ 14 writel(val, ldev->mmio + offset); 15} 16 17static 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 30static 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 43static 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 57static 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 71static 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 83static 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 95static 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 147free_algo_data: 148 DRM_ERROR("Failed to register i2c adapter %s\n", i2c_adapter->name); 149 kfree(i2c_algo_data); 150free_adapter: 151 kfree(i2c_adapter); 152 153 return ret; 154} 155 156int 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 169int 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