|
|
@@ -47,6 +47,7 @@
|
|
|
#define CAL_EVENT 7
|
|
|
#define CAL_TRIGGER 7
|
|
|
#define PER_TRIGGER 6
|
|
|
+#define DP83640_N_PINS 12
|
|
|
|
|
|
#define MII_DP83640_MICR 0x11
|
|
|
#define MII_DP83640_MISR 0x12
|
|
|
@@ -173,6 +174,37 @@ MODULE_PARM_DESC(chosen_phy, \
|
|
|
MODULE_PARM_DESC(gpio_tab, \
|
|
|
"Which GPIO line to use for which purpose: cal,perout,extts1,...,extts6");
|
|
|
|
|
|
+static void dp83640_gpio_defaults(struct ptp_pin_desc *pd)
|
|
|
+{
|
|
|
+ int i, index;
|
|
|
+
|
|
|
+ for (i = 0; i < DP83640_N_PINS; i++) {
|
|
|
+ snprintf(pd[i].name, sizeof(pd[i].name), "GPIO%d", 1 + i);
|
|
|
+ pd[i].index = i;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < GPIO_TABLE_SIZE; i++) {
|
|
|
+ if (gpio_tab[i] < 1 || gpio_tab[i] > DP83640_N_PINS) {
|
|
|
+ pr_err("gpio_tab[%d]=%hu out of range", i, gpio_tab[i]);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ index = gpio_tab[CALIBRATE_GPIO] - 1;
|
|
|
+ pd[index].func = PTP_PF_PHYSYNC;
|
|
|
+ pd[index].chan = 0;
|
|
|
+
|
|
|
+ index = gpio_tab[PEROUT_GPIO] - 1;
|
|
|
+ pd[index].func = PTP_PF_PEROUT;
|
|
|
+ pd[index].chan = 0;
|
|
|
+
|
|
|
+ for (i = EXTTS0_GPIO; i < GPIO_TABLE_SIZE; i++) {
|
|
|
+ index = gpio_tab[i] - 1;
|
|
|
+ pd[index].func = PTP_PF_EXTTS;
|
|
|
+ pd[index].chan = i - EXTTS0_GPIO;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/* a list of clocks and a mutex to protect it */
|
|
|
static LIST_HEAD(phyter_clocks);
|
|
|
static DEFINE_MUTEX(phyter_clocks_lock);
|
|
|
@@ -266,15 +298,22 @@ static u64 phy2txts(struct phy_txts *p)
|
|
|
return ns;
|
|
|
}
|
|
|
|
|
|
-static void periodic_output(struct dp83640_clock *clock,
|
|
|
- struct ptp_clock_request *clkreq, bool on)
|
|
|
+static int periodic_output(struct dp83640_clock *clock,
|
|
|
+ struct ptp_clock_request *clkreq, bool on)
|
|
|
{
|
|
|
struct dp83640_private *dp83640 = clock->chosen;
|
|
|
struct phy_device *phydev = dp83640->phydev;
|
|
|
- u32 sec, nsec, period;
|
|
|
+ u32 sec, nsec, pwidth;
|
|
|
u16 gpio, ptp_trig, trigger, val;
|
|
|
|
|
|
- gpio = on ? gpio_tab[PEROUT_GPIO] : 0;
|
|
|
+ if (on) {
|
|
|
+ gpio = 1 + ptp_find_pin(clock->ptp_clock, PTP_PF_PEROUT, 0);
|
|
|
+ if (gpio < 1)
|
|
|
+ return -EINVAL;
|
|
|
+ } else {
|
|
|
+ gpio = 0;
|
|
|
+ }
|
|
|
+
|
|
|
trigger = PER_TRIGGER;
|
|
|
|
|
|
ptp_trig = TRIG_WR |
|
|
|
@@ -291,13 +330,14 @@ static void periodic_output(struct dp83640_clock *clock,
|
|
|
ext_write(0, phydev, PAGE5, PTP_TRIG, ptp_trig);
|
|
|
ext_write(0, phydev, PAGE4, PTP_CTL, val);
|
|
|
mutex_unlock(&clock->extreg_lock);
|
|
|
- return;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
sec = clkreq->perout.start.sec;
|
|
|
nsec = clkreq->perout.start.nsec;
|
|
|
- period = clkreq->perout.period.sec * 1000000000UL;
|
|
|
- period += clkreq->perout.period.nsec;
|
|
|
+ pwidth = clkreq->perout.period.sec * 1000000000UL;
|
|
|
+ pwidth += clkreq->perout.period.nsec;
|
|
|
+ pwidth /= 2;
|
|
|
|
|
|
mutex_lock(&clock->extreg_lock);
|
|
|
|
|
|
@@ -310,8 +350,8 @@ static void periodic_output(struct dp83640_clock *clock,
|
|
|
ext_write(0, phydev, PAGE4, PTP_TDR, nsec >> 16); /* ns[31:16] */
|
|
|
ext_write(0, phydev, PAGE4, PTP_TDR, sec & 0xffff); /* sec[15:0] */
|
|
|
ext_write(0, phydev, PAGE4, PTP_TDR, sec >> 16); /* sec[31:16] */
|
|
|
- ext_write(0, phydev, PAGE4, PTP_TDR, period & 0xffff); /* ns[15:0] */
|
|
|
- ext_write(0, phydev, PAGE4, PTP_TDR, period >> 16); /* ns[31:16] */
|
|
|
+ ext_write(0, phydev, PAGE4, PTP_TDR, pwidth & 0xffff); /* ns[15:0] */
|
|
|
+ ext_write(0, phydev, PAGE4, PTP_TDR, pwidth >> 16); /* ns[31:16] */
|
|
|
|
|
|
/*enable trigger*/
|
|
|
val &= ~TRIG_LOAD;
|
|
|
@@ -319,6 +359,7 @@ static void periodic_output(struct dp83640_clock *clock,
|
|
|
ext_write(0, phydev, PAGE4, PTP_CTL, val);
|
|
|
|
|
|
mutex_unlock(&clock->extreg_lock);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
/* ptp clock methods */
|
|
|
@@ -424,18 +465,21 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
|
|
|
struct dp83640_clock *clock =
|
|
|
container_of(ptp, struct dp83640_clock, caps);
|
|
|
struct phy_device *phydev = clock->chosen->phydev;
|
|
|
- int index;
|
|
|
+ unsigned int index;
|
|
|
u16 evnt, event_num, gpio_num;
|
|
|
|
|
|
switch (rq->type) {
|
|
|
case PTP_CLK_REQ_EXTTS:
|
|
|
index = rq->extts.index;
|
|
|
- if (index < 0 || index >= N_EXT_TS)
|
|
|
+ if (index >= N_EXT_TS)
|
|
|
return -EINVAL;
|
|
|
event_num = EXT_EVENT + index;
|
|
|
evnt = EVNT_WR | (event_num & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
|
|
|
if (on) {
|
|
|
- gpio_num = gpio_tab[EXTTS0_GPIO + index];
|
|
|
+ gpio_num = 1 + ptp_find_pin(clock->ptp_clock,
|
|
|
+ PTP_PF_EXTTS, index);
|
|
|
+ if (gpio_num < 1)
|
|
|
+ return -EINVAL;
|
|
|
evnt |= (gpio_num & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
|
|
|
if (rq->extts.flags & PTP_FALLING_EDGE)
|
|
|
evnt |= EVNT_FALL;
|
|
|
@@ -448,8 +492,7 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
|
|
|
case PTP_CLK_REQ_PEROUT:
|
|
|
if (rq->perout.index != 0)
|
|
|
return -EINVAL;
|
|
|
- periodic_output(clock, rq, on);
|
|
|
- return 0;
|
|
|
+ return periodic_output(clock, rq, on);
|
|
|
|
|
|
default:
|
|
|
break;
|
|
|
@@ -458,6 +501,12 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
|
|
|
return -EOPNOTSUPP;
|
|
|
}
|
|
|
|
|
|
+static int ptp_dp83640_verify(struct ptp_clock_info *ptp, unsigned int pin,
|
|
|
+ enum ptp_pin_function func, unsigned int chan)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static u8 status_frame_dst[6] = { 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00 };
|
|
|
static u8 status_frame_src[6] = { 0x08, 0x00, 0x17, 0x0B, 0x6B, 0x0F };
|
|
|
|
|
|
@@ -875,6 +924,7 @@ static void dp83640_free_clocks(void)
|
|
|
mutex_destroy(&clock->extreg_lock);
|
|
|
mutex_destroy(&clock->clock_lock);
|
|
|
put_device(&clock->bus->dev);
|
|
|
+ kfree(clock->caps.pin_config);
|
|
|
kfree(clock);
|
|
|
}
|
|
|
|
|
|
@@ -894,12 +944,18 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
|
|
|
clock->caps.n_alarm = 0;
|
|
|
clock->caps.n_ext_ts = N_EXT_TS;
|
|
|
clock->caps.n_per_out = 1;
|
|
|
+ clock->caps.n_pins = DP83640_N_PINS;
|
|
|
clock->caps.pps = 0;
|
|
|
clock->caps.adjfreq = ptp_dp83640_adjfreq;
|
|
|
clock->caps.adjtime = ptp_dp83640_adjtime;
|
|
|
clock->caps.gettime = ptp_dp83640_gettime;
|
|
|
clock->caps.settime = ptp_dp83640_settime;
|
|
|
clock->caps.enable = ptp_dp83640_enable;
|
|
|
+ clock->caps.verify = ptp_dp83640_verify;
|
|
|
+ /*
|
|
|
+ * Convert the module param defaults into a dynamic pin configuration.
|
|
|
+ */
|
|
|
+ dp83640_gpio_defaults(clock->caps.pin_config);
|
|
|
/*
|
|
|
* Get a reference to this bus instance.
|
|
|
*/
|
|
|
@@ -950,6 +1006,13 @@ static struct dp83640_clock *dp83640_clock_get_bus(struct mii_bus *bus)
|
|
|
if (!clock)
|
|
|
goto out;
|
|
|
|
|
|
+ clock->caps.pin_config = kzalloc(sizeof(struct ptp_pin_desc) *
|
|
|
+ DP83640_N_PINS, GFP_KERNEL);
|
|
|
+ if (!clock->caps.pin_config) {
|
|
|
+ kfree(clock);
|
|
|
+ clock = NULL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
dp83640_clock_init(clock, bus);
|
|
|
list_add_tail(&phyter_clocks, &clock->list);
|
|
|
out:
|
|
|
@@ -1363,7 +1426,7 @@ static void __exit dp83640_exit(void)
|
|
|
}
|
|
|
|
|
|
MODULE_DESCRIPTION("National Semiconductor DP83640 PHY driver");
|
|
|
-MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.at>");
|
|
|
+MODULE_AUTHOR("Richard Cochran <richardcochran@gmail.com>");
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
module_init(dp83640_init);
|