gpio模拟mdc/mdio通信 - 足各火丁Examples

时间:2024-02-24 16:34:20

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");
View Code