gpio模拟mdc/mdio通信
本文主要是学习gpio模拟mdc/mdio通信。
运行环境是在ATMEL的sama5d35MCU,两个GPIO引脚模拟MDC/MDIO通信,读取百兆phy的寄存器的值。
1 #include<linux/init.h> 2 #include<linux/module.h> 3 #include<linux/kernel.h> 4 #include<linux/sched.h> 5 #include<linux/init.h> 6 #include<linux/sched.h> 7 #include<linux/completion.h> 8 #include <asm/system.h> 9 #include <linux/param.h> 10 #include<linux/gpio.h> 11 #include<linux/cdev.h> 12 #include<linux/fs.h> 13 #include<linux/device.h> 14 #include<linux/slab.h> 15 #include<asm/uaccess.h> 16 #include<linux/delay.h> 17 #include<linux/miscdevice.h> 18 19 20 /* bb:bit-bang,通过gpio引脚,用软件模拟通信*/ 21 22 #define MDIO 117 /* MDIO correspond PD21 */ 23 #define MDC 116 /* MDC correspond PD20 */ 24 #define MDIO_DELAY 250 25 #define MDIO_READ_DELAY 350 26 27 /* Or MII_ADDR_C45 into regnum for read/write on mii_bus to enable the 21 bit 28 * IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips. 29 * */ 30 #define MII_ADDR_C45 (1<<30) 31 32 #define MDIO_READ 2 33 #define MDIO_WRITE 1 34 35 #define MDIO_C45 (1<<15) 36 #define MDIO_C45_ADDR (MDIO_C45 | 0) 37 #define MDIO_C45_READ (MDIO_C45 | 3) 38 #define MDIO_C45_WRITE (MDIO_C45 | 1) 39 40 #define MDIO_SETUP_TIME 10 41 #define MDIO_HOLD_TIME 10 42 43 44 //#define READ_REG 0x37 45 //#define WRITE_REG 0x38 46 47 48 #define MDIO_C45_TEST 0 49 50 51 typedef struct gpio_ctrl_blk{ 52 int pin; 53 int value; 54 }gpio_cblk_t; 55 56 typedef struct phy_reg_blk{ 57 unsigned int phy_address; 58 unsigned int reg_address; 59 unsigned int reg_value; 60 }phy_reg_cblk_t; 61 62 63 #define MDIO_DEV_ID \'t\' 64 #define READ_REG _IOWR (MDIO_DEV_ID,0x37,phy_reg_cblk_t) 65 #define WRITE_REG _IOWR (MDIO_DEV_ID,0x38,phy_reg_cblk_t) 66 static void MDC_OUT(void); 67 static void MDIO_OUT(void); 68 static void MDIO_IN(void); 69 static void MDC_H(void); 70 static void MDC_L(void); 71 static int GET_MDIO(void); 72 static void SET_MDIO(int val); 73 74 75 /* 设置MDC为输出引脚,在MDC输出时钟之前设置 */ 76 static void MDC_OUT(void) 77 { 78 gpio_cblk_t gpio_dev; 79 gpio_dev.pin = MDC; 80 at91_set_gpio_output(gpio_dev.pin,1); 81 } 82 83 /* 设置MDIO的gpio引脚为输出引脚 */ 84 static void MDIO_OUT(void) 85 { 86 gpio_cblk_t gpio_dev; 87 gpio_dev.pin = MDIO; 88 at91_set_gpio_output(gpio_dev.pin,1); 89 } 90 91 /* 设置MDIO的gpio引脚为输入引脚 */ 92 static void MDIO_IN(void) 93 { 94 gpio_cblk_t gpio_dev; 95 gpio_dev.pin = MDIO; 96 at91_set_gpio_input(gpio_dev.pin,1); 97 } 98 99 /* MDC输出高电平,在MDC设置为输出后调用 */ 100 static void MDC_H(void) 101 { 102 gpio_cblk_t gpio_dev; 103 104 gpio_dev.pin = MDC; 105 gpio_dev.value = 1; 106 at91_set_gpio_value(gpio_dev.pin,gpio_dev.value); 107 } 108 109 /* MDC输出低电平,在MDC设置为输出后调用 */ 110 static void MDC_L(void) 111 { 112 gpio_cblk_t gpio_dev; 113 114 gpio_dev.pin = MDC; 115 gpio_dev.value = 0; 116 at91_set_gpio_value(gpio_dev.pin,gpio_dev.value); 117 } 118 119 /* 获得MDIO的数据,只获得一个bit */ 120 static int GET_MDIO(void) 121 { 122 gpio_cblk_t gpio_dev; 123 124 gpio_dev.pin = MDIO; 125 gpio_dev.value = at91_get_gpio_value(gpio_dev.pin); 126 127 return gpio_dev.value; 128 } 129 130 /* 设置MDIO的数据,一个bit */ 131 static void SET_MDIO(int val) 132 { 133 gpio_cblk_t gpio_dev; 134 135 gpio_dev.pin = MDIO; 136 gpio_dev.value = val; 137 at91_set_gpio_value(gpio_dev.pin,gpio_dev.value); 138 } 139 140 141 /* MDIO发送一个bit的数据,MDIO必须已经被配置为输出 */ 142 static void mdio_bb_send_bit(int val) 143 { 144 MDC_OUT(); 145 SET_MDIO(val); 146 ndelay(MDIO_DELAY); 147 MDC_L(); 148 ndelay(MDIO_DELAY); 149 MDC_H(); 150 } 151 152 /* MDIO 获取一个bit的数据,MDIO必须已经被配置为输入. */ 153 static int mdio_bb_get_bit(void) 154 { 155 int value; 156 157 MDC_OUT(); 158 ndelay(MDIO_DELAY); 159 MDC_L(); 160 ndelay(MDIO_READ_DELAY); 161 // ndelay(MDIO_DELAY); 162 MDC_H(); 163 164 value = GET_MDIO(); 165 166 return value; 167 } 168 169 /* 170 * MDIO发送一个数据,MDIO 必须被配置为输出模式. 171 * value:要发送的数据 172 * bits:数据的位数 173 * 174 * */ 175 static void mdio_bb_send_num(unsigned int value ,int bits) 176 { 177 int i; 178 MDIO_OUT(); 179 180 for(i = bits - 1; i >= 0; i--) 181 mdio_bb_send_bit((value >> i) & 1); 182 } 183 184 /* 185 * MDIO获取一个数据,MDIO 必须被配置为输入模式. 186 * bits:获取数据的位数 187 * 188 * */ 189 static int mdio_bb_get_num(int bits) 190 { 191 int i; 192 int ret = 0; 193 for(i = bits - 1; i >= 0; i--) 194 { 195 ret <<= 1; 196 ret |= mdio_bb_get_bit(); 197 } 198 199 return ret; 200 } 201 202 203 204 /* Utility to send the preamble, address, and 205 * register (common to read and write). 206 */ 207 static void mdio_bb_cmd(int op,int phy,int reg) 208 { 209 int i = 0 ; 210 MDIO_OUT(); //设置MDIO引脚为输出引脚 211 212 /*发送32bit的1,这个帧前缀域不是必须的,某些物理层芯片的MDIO操作就没有这个域*/ 213 for(i = 0; i < 32; i++) 214 mdio_bb_send_bit(1); 215 216 217 /* 发送开始位(01),和读操作码(10),写操作码(01) 218 * Clause 45 操作,开始位是(00),(11)为读,(10)为写 219 */ 220 221 #if MDIO_C45_TEST 222 mdio_bb_send_bit(0); 223 if(op & MDIO_C45) 224 mdio_bb_send_bit(0); 225 else 226 mdio_bb_send_bit(1); 227 228 229 #else 230 mdio_bb_send_bit(0); 231 mdio_bb_send_bit(1); 232 233 #endif 234 mdio_bb_send_bit((op >> 1) & 1); 235 mdio_bb_send_bit((op >> 0) & 1); 236 237 mdio_bb_send_num(phy,5); 238 mdio_bb_send_num(reg,5); 239 240 } 241 242 static int mdio_bb_cmd_addr(int phy,int addr) 243 { 244 unsigned int dev_addr = (addr >> 16) & 0x1F; 245 unsigned int reg = addr & 0xFFFF; 246 247 mdio_bb_cmd(MDIO_C45_ADDR,phy,dev_addr); 248 249 /* send the turnaround (10) */ 250 mdio_bb_send_bit(1); 251 mdio_bb_send_bit(0); 252 253 mdio_bb_send_num(reg,16); 254 255 MDIO_IN(); 256 mdio_bb_get_bit(); 257 258 return dev_addr; 259 } 260 261 void mdio_set_turnaround(void) 262 { 263 int i = 0; 264 265 MDIO_IN(); 266 MDC_OUT(); 267 for(i=0;i<2;i++) 268 { 269 ndelay(MDIO_DELAY); 270 MDC_L(); 271 ndelay(MDIO_DELAY); 272 MDC_H(); 273 } 274 } 275 276 static unsigned int mdio_bb_read(int phy,int reg) 277 { 278 unsigned int ret,i; 279 280 #if MDIO_C45_TEST 281 /* 寄存器是否满足有C45标志 */ 282 if(reg & MII_ADDR_C45) 283 { 284 reg = mdio_bb_cmd_addr(phy,reg); 285 mdio_bb_cmd(MDIO_C45_READ,phy,reg); 286 } 287 else 288 mdio_bb_cmd(MDIO_READ,phy,reg); 289 #else 290 mdio_bb_cmd(MDIO_READ,phy,reg); 291 #endif 292 MDIO_IN(); 293 //mdio_set_turnaround(); 294 /* check the turnaround bit: the PHY should be driving it to zero */ 295 if(mdio_bb_get_bit() != 0) 296 { 297 /* PHY didn\'t driver TA low -- flush any bits it may be trying to send*/ 298 for(i = 0; i < 32; i++) 299 mdio_bb_get_bit(); 300 return 0xFFFF; 301 } 302 303 ret = mdio_bb_get_num(16); 304 mdio_bb_get_bit(); 305 306 return ret; 307 } 308 309 310 311 static int mdio_bb_write(unsigned int phy,unsigned int reg,unsigned int val) 312 { 313 #if MDIO_C45_TEST 314 if(reg & MII_ADDR_C45) 315 { 316 reg = mdio_bb_cmd_addr(phy,reg); 317 mdio_bb_cmd(MDIO_C45_WRITE,phy,reg); 318 } 319 else 320 mdio_bb_cmd(MDIO_WRITE,phy,reg); 321 #else 322 mdio_bb_cmd(MDIO_WRITE,phy,reg); 323 #endif 324 325 326 #if 1 327 /* send the turnaround (10) */ 328 mdio_bb_send_bit(1); 329 mdio_bb_send_bit(0); 330 #else 331 mdio_set_turnaround(); 332 #endif 333 mdio_bb_send_num(val,16); 334 335 MDIO_IN(); 336 //mdio_bb_get_bit(); 337 338 return 0; 339 } 340 341 342 static int mdio_ctrl_drv_open(struct inode *inode, struct file *file ) 343 { 344 return 0; 345 } 346 347 static int mdio_ctrl_drv_release(struct inode *inode, struct file *file ) 348 { 349 return 0; 350 } 351 352 static long mdio_ctrl_drv_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 353 { 354 phy_reg_cblk_t phy_reg; 355 int ret = 0; 356 357 void __user *argp = (void __user *)arg; 358 if( argp==NULL ) 359 { 360 return -EFAULT; 361 } 362 363 if (copy_from_user(&phy_reg, argp, sizeof(phy_reg_cblk_t))) { 364 return -EFAULT; 365 } 366 367 switch (cmd) { 368 case READ_REG: 369 phy_reg.reg_value = mdio_bb_read(phy_reg.phy_address,phy_reg.reg_address); 370 if(copy_to_user(argp,&phy_reg,sizeof(phy_reg_cblk_t))) 371 { 372 return -EFAULT; 373 } 374 break; 375 case WRITE_REG: 376 ret = mdio_bb_write(phy_reg.phy_address,phy_reg.reg_address,phy_reg.reg_value); 377 default: 378 return -EINVAL; 379 380 } 381 382 return 0; 383 } 384 385 static struct file_operations mdio_ctl_drv_fileops = { 386 .owner = THIS_MODULE, 387 .open = mdio_ctrl_drv_open, 388 .unlocked_ioctl = mdio_ctrl_drv_unlocked_ioctl, 389 .release = mdio_ctrl_drv_release 390 }; 391 392 static struct miscdevice mdio_dev = { 393 MISC_DYNAMIC_MINOR, 394 "mdio_dev", 395 &mdio_ctl_drv_fileops, 396 }; 397 398 399 int mdio_ctrl_drv_module_init(void) 400 { 401 int ret = 0; 402 403 ret = misc_register(&mdio_dev); 404 if(ret != 0) 405 { 406 ret = -EFAULT; 407 return ret; 408 } 409 printk("mdio_drv_init ok\n"); 410 return 0; 411 } 412 413 414 void mdio_ctrl_drv_module_exit(void) 415 { 416 misc_deregister(&mdio_dev); 417 printk("mdio_drv_exit ok\n"); 418 } 419 420 421 422 module_init(mdio_ctrl_drv_module_init); 423 module_exit(mdio_ctrl_drv_module_exit); 424 MODULE_LICENSE("GPL");