spbx/roms/srcs/images/ethernet/gianfar/gianfar_1588.c

979 lines
27 KiB
C

/*
* drivers/net/gianfar_1588.c
*
* Copyright 2008-2011 Freescale Semiconductor, Inc.
* Copyright 2009 IXXAT Automation, GmbH
*
* Author: Anup Gangwar <anup.gangwar@freescale.com>
* Yashpal Dutta <yashpal.dutta@freescale.com>
*
* Gianfar Ethernet Driver -- IEEE 1588 interface functionality
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/vmalloc.h>
#include <linux/of.h>
#include "gianfar.h"
#include <linux/of_platform.h>
#if defined(CONFIG_1588_MUX_eTSEC1) || defined(CONFIG_1588_MUX_eTSEC2)
#define MPC85XX_PMUXCR_OFFS 0x60
#if defined(CONFIG_1588_MUX_eTSEC1)
#define PMUXCR_eTSEC1_PPS 0x10000000
#endif
#if defined(CONFIG_1588_MUX_eTSEC2)
#define PMUXCR_eTSEC2_PPS 0x08000000
#endif
#endif
static int gfar_ptp_init_circ(struct gfar_ptp_circular_t* buf);
static int gfar_ptp_is_empty(struct gfar_ptp_circular_t* buf);
static int gfar_ptp_nelems(struct gfar_ptp_circular_t* buf);
static int gfar_ptp_is_full(struct gfar_ptp_circular_t* buf);
static int gfar_ptp_insert(struct gfar_ptp_circular_t* buf,
struct gfar_ptp_data_t* data);
static int gfar_ptp_find_and_remove(struct gfar_ptp_circular_t* buf,
int key, struct gfar_ptp_data_t* data);
static u32 nominal_frequency(u32 sysclock_freq);
static int gfar_ptp_cal_attr(struct gfar_ptp_attr_t* ptp_attr);
static DECLARE_WAIT_QUEUE_HEAD(ptp_rx_ts_wait);
#define PTP_GET_RX_TIMEOUT (HZ/10)
static u32 freq_compensation;
static struct proc_dir_entry* gfar_1588_proc_file;
static struct gfar_node_info_t gfar_node;
static struct gfar_1588_data_t gfar_1588_data;
/*64 bites add and return the result*/
static u64 add64_oper(u64 addend, u64 augend)
{
u64 result = 0;
u32 addendh, addendl, augendl, augendh;
addendh = (u32)(addend >> 32);
addendl = (u32)addend;
augendh = (u32)(augend >> 32);
augendl = (u32)augend;
asm("addc %0,%1,%2" : "=r"(addendl) : "r"(addendl), "r"(augendl));
asm("adde %0,%1,%2" : "=r"(addendh) : "r"(addendh), "r"(augendh));
result = (((u64)addendh << 32) | (u64)addendl);
return result;
}
/*64 bits multiplication and return the result*/
static u64 multi64_oper(u32 multiplier, u32 multiplicand)
{
u64 result = 0;
u64 tmp_ret = 0;
u32 tmp_multi = multiplicand;
int i;
for(i = 0; i < 32; i++)
{
if(tmp_multi & 0x1)
{
tmp_ret = ((u64)multiplier << i);
result = add64_oper(result, tmp_ret);
}
tmp_multi = (tmp_multi >> 1);
}
return result;
}
static u32 div64_oper(u64 dividend, u32 divisor, u32* quotient)
{
u32 time_h, time_l;
u32 result;
u64 tmp_dividend;
int i;
*quotient = 0;
time_h = (u32)(dividend >> 32);
time_l = (u32)dividend;
time_h = time_h % divisor;
for(i = 1; i <= 32; i++)
{
tmp_dividend = (((u64)time_h << 32) | (u64)time_l);
tmp_dividend = (tmp_dividend << 1);
time_h = (u32)(tmp_dividend >> 32);
time_l = (u32)tmp_dividend;
result = time_h / divisor;
time_h = time_h % divisor;
*quotient += (result << (32 - i));
}
return time_h;
}
/*
* Resource required for accessing 1588 Timer Registers. There are few 1588
* modules registers which are present in eTSEC1 memory space only. The second
* reg entry there in denotes the 1588 regs.
*/
int gfar_ptp_init(struct gfar_private* priv)
{
priv->ptimer = ioremap(priv->timer_resource.start,
sizeof(struct gfar_regs_1588));
if((priv->ptimer == NULL) ||
gfar_ptp_init_circ(&(priv->rx_time_sync)) ||
gfar_ptp_init_circ(&(priv->rx_time_del_req)) ||
gfar_ptp_init_circ(&(priv->rx_time_pdel_req)) ||
gfar_ptp_init_circ(&(priv->rx_time_pdel_resp)))
{
return 1;
}
return 0;
}
void gfar_ptp_cleanup(struct gfar_private* priv)
{
if(priv->ptimer != NULL)
{
iounmap(priv->ptimer);
}
if(priv->rx_time_sync.data_buf)
{
vfree(priv->rx_time_sync.data_buf);
}
if(priv->rx_time_del_req.data_buf)
{
vfree(priv->rx_time_del_req.data_buf);
}
if(priv->rx_time_pdel_req.data_buf)
{
vfree(priv->rx_time_pdel_req.data_buf);
}
if(priv->rx_time_pdel_resp.data_buf)
{
vfree(priv->rx_time_pdel_resp.data_buf);
}
}
int gfar_ptp_do_txstamp(struct sk_buff* skb)
{
u16* udp_port;
char* pkt_type;
if(skb->len > 44)
{
pkt_type = (char*)(skb->data + GFAR_PTP_PKT_TYPE_OFFS);
udp_port = (u16*)(skb->data + GFAR_PTP_PORT_OFFS);
/* Check if port is 319 for PTP Event, and check for UDP */
if((*udp_port == 0x013F) &&
(*pkt_type == GFAR_PACKET_TYPE_UDP))
{
return 1;
}
}
return 0;
}
void gfar_ptp_store_rxstamp(struct net_device* dev, struct sk_buff* skb)
{
int msg_type, seq_id, control;
struct gfar_ptp_data_t tmp_rx_time;
struct gfar_private* priv = netdev_priv(dev);
u16 udp_port;
char pkt_type;
pkt_type = *(((char*)skb->data) + GFAR_PTP_PKT_TYPE_OFFS);
udp_port = *((u16*)(skb->data + GFAR_PTP_PORT_OFFS));
seq_id = *((u16*)(skb->data + GFAR_PTP_SEQ_ID_OFFS));
control = *((u8*)(skb->data + GFAR_PTP_CTRL_OFFS));
/* Check if port is 319 for PTP Event, and check for UDP */
if((udp_port == 0x13F) && (pkt_type == GFAR_PACKET_TYPE_UDP))
{
tmp_rx_time.key = seq_id;
tmp_rx_time.item.high = *((u32*)skb->data);
tmp_rx_time.item.low = *(((u32*)skb->data) + 1);
switch(control)
{
case GFAR_PTP_CTRL_SYNC:
gfar_ptp_insert(&(priv->rx_time_sync), &tmp_rx_time);
break;
case GFAR_PTP_CTRL_DEL_REQ:
gfar_ptp_insert(&(priv->rx_time_del_req), &tmp_rx_time);
break;
/* clear transportSpecific field*/
case GFAR_PTP_CTRL_ALL_OTHER:
msg_type = (*((u8*)(skb->data +
GFAR_PTP_MSG_TYPE_OFFS))) & 0x0F;
switch(msg_type)
{
case GFAR_PTP_MSG_TYPE_PDREQ:
gfar_ptp_insert(&(priv->rx_time_pdel_req),
&tmp_rx_time);
break;
case GFAR_PTP_MSG_TYPE_PDRESP:
gfar_ptp_insert(&(priv->rx_time_pdel_resp),
&tmp_rx_time);
break;
default:
break;
}
break;
default:
break;
}
wake_up_interruptible(&ptp_rx_ts_wait);
}
}
int gfar_ptp_init_circ(struct gfar_ptp_circular_t* buf)
{
buf->data_buf = (struct gfar_ptp_data_t*)
vmalloc((DEFAULT_PTP_RX_BUF_SZ + 1) *
sizeof(struct gfar_ptp_data_t));
if(!buf->data_buf)
{
return 1;
}
buf->front = 0;
buf->end = 0;
buf->size = (DEFAULT_PTP_RX_BUF_SZ + 1);
return 0;
}
static inline int gfar_ptp_calc_index(int size, int curr_index, int offset)
{
return (curr_index + offset) % size;
}
int gfar_ptp_is_empty(struct gfar_ptp_circular_t* buf)
{
return (buf->front == buf->end);
}
int gfar_ptp_nelems(struct gfar_ptp_circular_t* buf)
{
const int front = buf->front;
const int end = buf->end;
const int size = buf->size;
int n_items;
if(end > front)
{
n_items = end - front;
}
else if(end < front)
{
n_items = size - (front - end);
}
else
{
n_items = 0;
}
return n_items;
}
int gfar_ptp_is_full(struct gfar_ptp_circular_t* buf)
{
if(gfar_ptp_nelems(buf) == (buf->size - 1))
{
return 1;
}
else
{
return 0;
}
}
int gfar_ptp_insert(struct gfar_ptp_circular_t* buf,
struct gfar_ptp_data_t* data)
{
struct gfar_ptp_data_t* tmp;
if(gfar_ptp_is_full(buf))
{
return 1;
}
tmp = (buf->data_buf + buf->end);
tmp->key = data->key;
tmp->item.high = data->item.high;
tmp->item.low = data->item.low;
buf->end = gfar_ptp_calc_index(buf->size, buf->end, 1);
return 0;
}
int gfar_ptp_find_and_remove(struct gfar_ptp_circular_t* buf,
int key, struct gfar_ptp_data_t* data)
{
int i;
int size = buf->size, end = buf->end;
if(gfar_ptp_is_empty(buf))
{
return 1;
}
i = buf->front;
while(i != end)
{
if((buf->data_buf + i)->key == key)
{
break;
}
i = gfar_ptp_calc_index(size, i, 1);
}
if(i == end)
{
buf->front = buf->end;
return 1;
}
data->item.high = (buf->data_buf + i)->item.high;
data->item.low = (buf->data_buf + i)->item.low;
buf->front = gfar_ptp_calc_index(size, i, 1);
return 0;
}
/* Set the 1588 timer counter registers */
static void gfar_set_1588cnt(struct net_device* dev,
struct gfar_ptp_time* gfar_time)
{
struct gfar_private* priv = netdev_priv(dev);
u32 tempval;
u64 alarm_value = 0, temp_alarm_val;
struct gfar_ptp_attr_t ptp_attr;
memset(&ptp_attr, 0, sizeof(struct gfar_ptp_attr_t));
temp_alarm_val = add64_oper((u64)gfar_time->low,
((u64)gfar_time->high) << 32);
div64_oper(temp_alarm_val, TMR_SEC, &tempval);
alarm_value = multi64_oper(tempval, TMR_SEC);
temp_alarm_val = add64_oper((u64)TMR_ALARM1_L,
((u64)TMR_ALARM1_H) << 32);
alarm_value = add64_oper(alarm_value, temp_alarm_val);
/* We must write the tmr_cnt_l register first */
tempval = (u32)gfar_time->low;
gfar_write(&priv->ptimer->tmr_cnt_l, tempval);
tempval = (u32)gfar_time->high;
gfar_write(&priv->ptimer->tmr_cnt_h, tempval);
tempval = (u32)alarm_value;
gfar_write(&(priv->ptimer->tmr_alarm1_l), tempval);
tempval = (u32)(alarm_value >> 32);
gfar_write(&(priv->ptimer->tmr_alarm1_h), tempval);
if(gfar_ptp_cal_attr(&ptp_attr))
{
return;
}
gfar_write(&(priv->ptimer->tmr_fiper1), ptp_attr.tmr_fiper1);
}
/* Get both the time-stamps and use the larger one */
static void gfar_get_tx_timestamp(struct gfar __iomem* regs,
struct gfar_ptp_time* tx_time)
{
struct gfar_ptp_time tx_set_1, tx_set_2;
u32 tmp;
/* Read the low register first */
tx_set_1.low = gfar_read(&regs->tmr_txts1_l);
tx_set_1.high = gfar_read(&regs->tmr_txts1_h);
tx_set_2.low = gfar_read(&regs->tmr_txts2_l);
tx_set_2.high = gfar_read(&regs->tmr_txts2_h);
tmp = 0;
if(tx_set_2.high > tx_set_1.high)
{
tmp = 1;
}
else if(tx_set_2.high == tx_set_1.high)
if(tx_set_2.low > tx_set_1.low)
{
tmp = 1;
}
if(tmp == 0)
{
tx_time->low = tx_set_1.low;
tx_time->high = tx_set_1.high;
}
else
{
tx_time->low = tx_set_2.low;
tx_time->high = tx_set_2.high;
}
}
static uint8_t gfar_get_rx_time(struct gfar_private* priv, struct ifreq* ifr,
struct gfar_ptp_time* rx_time, int mode)
{
struct gfar_ptp_data_t tmp;
int key, flag;
key = *((int*)ifr->ifr_data);
switch(mode)
{
case PTP_GET_RX_TIMESTAMP_SYNC:
flag = gfar_ptp_find_and_remove(&(priv->rx_time_sync),
key, &tmp);
break;
case PTP_GET_RX_TIMESTAMP_DEL_REQ:
flag = gfar_ptp_find_and_remove(&(priv->rx_time_del_req),
key, &tmp);
break;
case PTP_GET_RX_TIMESTAMP_PDELAY_REQ:
flag = gfar_ptp_find_and_remove(&(priv->rx_time_pdel_req),
key, &tmp);
break;
case PTP_GET_RX_TIMESTAMP_PDELAY_RESP:
flag = gfar_ptp_find_and_remove(&(priv->rx_time_pdel_resp),
key, &tmp);
break;
default:
flag = 1;
printk(KERN_ERR "ERROR\n");
break;
}
if(!flag)
{
rx_time->high = tmp.item.high;
rx_time->low = tmp.item.low;
return 0;
}
else
{
wait_event_interruptible_timeout(ptp_rx_ts_wait, 0,
PTP_GET_RX_TIMEOUT);
switch(mode)
{
case PTP_GET_RX_TIMESTAMP_SYNC:
flag = gfar_ptp_find_and_remove(&(priv->rx_time_sync),
key, &tmp);
break;
case PTP_GET_RX_TIMESTAMP_DEL_REQ:
flag = gfar_ptp_find_and_remove(
&(priv->rx_time_del_req), key, &tmp);
break;
case PTP_GET_RX_TIMESTAMP_PDELAY_REQ:
flag = gfar_ptp_find_and_remove(
&(priv->rx_time_pdel_req), key, &tmp);
break;
case PTP_GET_RX_TIMESTAMP_PDELAY_RESP:
flag = gfar_ptp_find_and_remove(
&(priv->rx_time_pdel_resp), key, &tmp);
break;
}
if(flag == 0)
{
rx_time->high = tmp.item.high;
rx_time->low = tmp.item.low;
return 0;
}
return -1;
}
}
static void gfar_get_curr_cnt(struct gfar_regs_1588 __iomem* ptimer,
struct gfar_ptp_time* curr_time)
{
curr_time->low = gfar_read(&ptimer->tmr_cnt_l);
curr_time->high = gfar_read(&ptimer->tmr_cnt_h);
}
/*set Fiper Trigger Alarm */
void gfar_set_fiper_alarm(struct net_device* dev, struct gfar_ptp_time* alarm)
{
struct gfar_private* priv = netdev_priv(dev);
struct gfar_ptp_attr_t ptp_attr;
memset(&ptp_attr, 0, sizeof(struct gfar_ptp_attr_t));
gfar_write(&(priv->ptimer->tmr_alarm1_l), alarm->low);
gfar_write(&(priv->ptimer->tmr_alarm1_h), alarm->high);
if(gfar_ptp_cal_attr(&ptp_attr))
{
return;
}
gfar_write(&(priv->ptimer->tmr_fiper1), ptp_attr.tmr_fiper1);
}
int gfar_ioctl_1588(struct net_device* dev, struct ifreq* ifr, int cmd)
{
struct gfar_private* priv = netdev_priv(dev);
struct gfar __iomem* regs = priv->gfargrp[0].regs;
struct gfar_ptp_time* cnt, *alarm;
signed long* p_addend;
struct gfar_ptp_time rx_time, tx_time, curr_time;
int retval = 0;
switch(cmd)
{
case PTP_GET_RX_TIMESTAMP_SYNC:
case PTP_GET_RX_TIMESTAMP_DEL_REQ:
case PTP_GET_RX_TIMESTAMP_PDELAY_REQ:
case PTP_GET_RX_TIMESTAMP_PDELAY_RESP:
retval = gfar_get_rx_time(priv, ifr, &rx_time, cmd);
if(retval == 0)
{
copy_to_user(ifr->ifr_data, &rx_time, sizeof(rx_time));
}
break;
case PTP_GET_TX_TIMESTAMP:
gfar_get_tx_timestamp(regs, &tx_time);
copy_to_user(ifr->ifr_data, &tx_time, sizeof(tx_time));
break;
case PTP_GET_CNT:
gfar_get_curr_cnt(priv->ptimer, &curr_time);
copy_to_user(ifr->ifr_data, &curr_time, sizeof(curr_time));
break;
case PTP_SET_CNT:
cnt = (struct gfar_ptp_time*)ifr->ifr_data;
gfar_set_1588cnt(dev, cnt);
break;
case PTP_SET_FIPER_ALARM:
alarm = (struct gfar_ptp_time*)ifr->ifr_data;
gfar_set_fiper_alarm(dev, alarm);
break;
case PTP_ADJ_ADDEND:
p_addend = (signed long*)ifr->ifr_data;
/* assign new value directly */
gfar_write(&priv->ptimer->tmr_add, *p_addend);
break;
case PTP_GET_ADDEND:
/* return initial timer add value
* to calculate drift correction */
copy_to_user(ifr->ifr_data, &freq_compensation,
sizeof(freq_compensation));
break;
case PTP_CLEANUP_TIMESTAMP_BUFFERS:
/* reset sync buffer */
priv->rx_time_sync.front = 0;
priv->rx_time_sync.end = 0;
priv->rx_time_sync.size = (DEFAULT_PTP_RX_BUF_SZ + 1);
/* reset delay_req buffer */
priv->rx_time_del_req.front = 0;
priv->rx_time_del_req.end = 0;
priv->rx_time_del_req.size = (DEFAULT_PTP_RX_BUF_SZ + 1);
/* reset pdelay_req buffer */
priv->rx_time_pdel_req.front = 0;
priv->rx_time_pdel_req.end = 0;
priv->rx_time_pdel_req.size = (DEFAULT_PTP_RX_BUF_SZ + 1);
/* reset pdelay_resp buffer */
priv->rx_time_pdel_resp.front = 0;
priv->rx_time_pdel_resp.end = 0;
priv->rx_time_pdel_resp.size = (DEFAULT_PTP_RX_BUF_SZ + 1);
break;
default:
return -EINVAL;
}
return retval;
}
/* 1588 Module intialization and filer table populating routine*/
void gfar_1588_start(struct net_device* dev)
{
struct gfar_private* priv = netdev_priv(dev);
struct gfar_ptp_attr_t ptp_attr;
u32* tmr_prsc, *cksel;
struct device_node* np;
memset(&ptp_attr, 0, sizeof(struct gfar_ptp_attr_t));
np = of_find_compatible_node(NULL, NULL, "fsl,gianfar-ptp-timer");
if(np == NULL)
{
printk(KERN_ERR "1588: Cannot find gianfar-ptp-timer node \r\n");
return;
}
if(gfar_ptp_cal_attr(&ptp_attr))
{
return;
}
tmr_prsc = (u32*)of_get_property(np, "tmr-prsc", NULL);
if(tmr_prsc == NULL)
{
printk(KERN_ERR "1588: Cannot find tmr-prsc property \r\n");
return;
}
gfar_write(&(priv->ptimer->tmr_prsc), *tmr_prsc);
gfar_write(&(priv->ptimer->tmr_fiper1), ptp_attr.tmr_fiper1);
gfar_write(&(priv->ptimer->tmr_alarm1_l), TMR_ALARM1_L);
gfar_write(&(priv->ptimer->tmr_alarm1_h), TMR_ALARM1_H);
/* Need to mask the TCLK bits as they are initialized with 1 */
gfar_write(&(priv->ptimer->tmr_ctrl),
(gfar_read(&(priv->ptimer->tmr_ctrl))
& ~TMR_CTRL_TCLK_MASK) | (ptp_attr.tclk_period));
/* initialize TMR_ADD with the initial frequency compensation value:
* freq_compensation = (2^32 / frequency ratio)
*/
div64_oper((((u64)2 << 31) * 100),
ptp_attr.freq_div_ratio, &freq_compensation);
gfar_write(&(priv->ptimer->tmr_add), freq_compensation);
cksel = (u32*)of_get_property(np, "cksel", NULL);
if(cksel == NULL)
{
printk(KERN_ERR "1588: Cannot find cksel property \r\n");
return;
}
gfar_write(&(priv->ptimer->tmr_ctrl),
gfar_read(&(priv->ptimer->tmr_ctrl)) |
TMR_CTRL_ENABLE | *cksel | TMR_CTRL_FIPER_START);
}
/* Cleanup routine for 1588 module.
* When PTP is disabled this routing is called */
void gfar_1588_stop(struct net_device* dev)
{
struct gfar_private* priv = netdev_priv(dev);
gfar_write(&priv->ptimer->tmr_ctrl,
gfar_read(&priv->ptimer->tmr_ctrl)
& ~TMR_CTRL_ENABLE);
}
void pmuxcr_guts_write(void)
{
#if defined(CONFIG_1588_MUX_eTSEC1) || defined(CONFIG_1588_MUX_eTSEC2)
struct device_node* np = NULL;
void __iomem* immap = NULL;
u32 pmuxcr;
np = of_find_compatible_node(NULL, NULL, "fsl,p2020-guts");
immap = of_iomap(np, 0);
if(immap)
{
pmuxcr = in_be32(immap + MPC85XX_PMUXCR_OFFS);
#if defined(CONFIG_1588_MUX_eTSEC1)
pmuxcr |= PMUXCR_eTSEC1_PPS;
#endif
#if defined(CONFIG_1588_MUX_eTSEC2)
pmuxcr |= PMUXCR_eTSEC2_PPS;
#endif
out_be32(immap + MPC85XX_PMUXCR_OFFS, pmuxcr);
iounmap(immap);
}
else
{
printk(KERN_INFO "1588 muxing could not be done,"
" mapping failed\n");
}
#endif
}
/**
* nominal_frequency - This function calculates the nominal frequency.
* nominal frequency is the desired clock frequency.
* @sysclock_freq: Timer Oscillator Frequency
*
* Description:
* Returns the nominal frequency which is calculated on the following
* basis.
* nominal frequency should be less than the Timer Oscillator frequency.
* nominal frequency should be a factor of 1000.
*
* Eg If Timer Oscillator frequency is 400.
* then nominal frequency can be 250.
*
* If Timer Oscillator frequency is 600.
* then nominal frequency can be 500.
*
* If Timer Oscillator frequency is 333.
* then nominal frequency can be 250.
*/
u32 nominal_frequency(u32 sysclock_freq)
{
u32 remainder = 0;
remainder = sysclock_freq % 50;
if(remainder != 0)
{
sysclock_freq = sysclock_freq - remainder;
sysclock_freq += 50;
}
while((10000 % (sysclock_freq -= 50)) != 0)
{
continue;
}
return sysclock_freq;
}
int gfar_ptp_cal_attr(struct gfar_ptp_attr_t* ptp_attr)
{
u32* sysclock_freq, nominal_freq, tclk_period;
struct device_node* np;
np = of_find_compatible_node(NULL, NULL, "fsl,gianfar-ptp-timer");
if(np == NULL)
{
printk(KERN_ERR "1588: Cannot find gianfar-ptp-timer node \r\n");
return 1;
}
sysclock_freq = (u32*)of_get_property(np, "timer-frequency", NULL);
if(sysclock_freq == NULL)
{
printk(KERN_DEBUG "1588: Cannot find timer-frequency property \r\n");
return 1;
}
#if 0
printk(KERN_DEBUG "1588 is running at system-clock"
" frequency (%u) \r\n", *sysclock_freq);
#endif
nominal_freq = nominal_frequency(DIV_ROUND(*sysclock_freq, 1000) \
/ 1000);
/* TCLK_PERIOD = 10^9/Nominal_Frequency in MHZ */
tclk_period = 1000 / nominal_freq;
/* FIPER = (10^9 / (Required PPS * TCLK_PERIOD)) - TCLK_PERIOD*/
ptp_attr->tmr_fiper1 = (ONE_GIGA / (PPS_1588 * tclk_period)) \
- tclk_period;
tclk_period <<= 16;
ptp_attr->tclk_period = tclk_period;
ptp_attr->nominal_freq = nominal_freq;
ptp_attr->sysclock_freq = DIV_ROUND(*sysclock_freq, 1000) / 1000;
/*
* FreqDivRatio = Timer Oscillator Freq / Nominal Freq
* and Timer Oscillator Freq = System Clock Freq
*/
ptp_attr->freq_div_ratio = (ptp_attr->sysclock_freq *
100) / ptp_attr->nominal_freq;
return 0;
}
static int gfar_1588_proc_read(char* buffer,
char** buffer_location,
off_t offset, int buffer_length, int* eof, void* data)
{
int len;
struct gfar_1588_data_t* gfar_1588_info = \
(struct gfar_1588_data_t*)data;
len = sprintf(buffer, "%s = '%s'\n",
gfar_1588_info->name, gfar_1588_info->value);
return len;
}
static int gfar_1588_proc_write(struct file* file, const char* buffer,
unsigned long count, void* data)
{
int i, cnt;
struct gfar_private* priv;
struct device_node* np = NULL;
struct of_device* ofdev;
struct gfar_1588_data_t* gfar_1588_info = \
(struct gfar_1588_data_t*)data;
struct gfar __iomem* regs;
np = of_find_compatible_node(NULL, NULL, "fsl,gianfar-ptp-timer");
if(np == NULL)
{
printk(KERN_ERR "1588: Cannot find ptp_timer node. Check"
" ptp_timer node in dts \r\n");
return -EINVAL;
}
if(count > GFAR_1588_PROCFS_MAX_SIZE)
{
count = GFAR_1588_PROCFS_MAX_SIZE;
}
if(copy_from_user(gfar_1588_info->value, buffer, count))
{
return -EFAULT;
}
gfar_1588_info->value[count - 1] = '\0';
printk(KERN_DEBUG "\r\n buffer (%s) \r\n", gfar_1588_info->value);
cnt = gfar_node.match_cnt / sizeof(struct of_device_id);
/*
* Here we are getting the valid index ie "i" for which we have
* valid compatible string in the dts file.
*/
for(i = 0; i < (cnt - 1); i++)
{
printk(KERN_DEBUG "\r\n gfar-node is (%s)\r\n", \
gfar_node.gfar_node_match[i].compatible);
if(!of_find_compatible_node(NULL, NULL, \
gfar_node.gfar_node_match[i].compatible))
{
continue;
}
else
{
break;
}
}
/*
* After getting the valid index "i" from above for loop
* we are Enabling/Disabling the 1588 functionality on the
* eTSEC controllers.
*/
while(1)
{
np = of_find_compatible_node(np, NULL, \
gfar_node.gfar_node_match[i].compatible);
if(np == NULL)
{
printk(KERN_DEBUG "\r\nUnale to find gfar-node\n");
break;
}
else
{
ofdev = of_find_device_by_node(np);
priv = dev_get_drvdata(&ofdev->dev);
regs = priv->gfargrp[0].regs;
if(!strcmp(gfar_1588_info->value, "0"))
{
printk(KERN_DEBUG "\r\n PTPD OFF \r\n");
priv->ptimer_present = 0;
gfar_write(&priv->ptimer->tmr_ctrl,
gfar_read(&priv->ptimer->tmr_ctrl)
& ~TMR_CTRL_ENABLE);
gfar_write(&regs->rctrl,
gfar_read(&regs->rctrl)
& ~RCTRL_TS_ENABLE);
}
else if(!strcmp(gfar_1588_info->value, "1"))
{
printk(KERN_DEBUG "\r\n PTPD ON \r\n");
priv->ptimer_present = 1;
gfar_write(&priv->ptimer->tmr_ctrl,
gfar_read(&priv->ptimer->tmr_ctrl)
| TMR_CTRL_ENABLE);
gfar_write(&regs->rctrl,
gfar_read(&regs->rctrl)
| RCTRL_TS_ENABLE);
}
}
}
return count;
}
void gfar_1588_proc_init(struct of_device_id* dev_id, int cnt)
{
gfar_1588_proc_file = create_proc_entry(GFAR_1588_PROCFS_NAME, \
0644, NULL);
if(gfar_1588_proc_file == NULL)
{
remove_proc_entry(GFAR_1588_PROCFS_NAME, NULL);
printk(KERN_ALERT "Error: Could not initialize /proc/%s\n",
GFAR_1588_PROCFS_NAME);
return;
}
strcpy(gfar_1588_data.name, GFAR_1588_PROCFS_NAME);
strcpy(gfar_1588_data.value, "1");
gfar_1588_proc_file->read_proc = gfar_1588_proc_read;
gfar_1588_proc_file->write_proc = gfar_1588_proc_write;
gfar_1588_proc_file->mode = S_IFREG | S_IRUGO;
gfar_1588_proc_file->uid = 0;
gfar_1588_proc_file->gid = 0;
gfar_1588_proc_file->data = &gfar_1588_data;
gfar_node.gfar_node_match = dev_id;
gfar_node.match_cnt = cnt;
printk(KERN_INFO "/proc/%s created \r\n", GFAR_1588_PROCFS_NAME);
}
void gfar_1588_proc_exit()
{
remove_proc_entry(GFAR_1588_PROCFS_NAME, NULL);
printk(KERN_INFO "/proc/%s removed \r\n", GFAR_1588_PROCFS_NAME);
}