Merge tag 'dt-3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[cascardo/linux.git] / drivers / net / phy / dp83640.c
index 98e7cbf..6a999e6 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/module.h>
 #include <linux/net_tstamp.h>
 #include <linux/netdevice.h>
+#include <linux/if_vlan.h>
 #include <linux/phy.h>
 #include <linux/ptp_classify.h>
 #include <linux/ptp_clock_kernel.h>
@@ -47,6 +48,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 +175,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 +299,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 +331,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 +351,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 +360,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 +466,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 +493,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 +502,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 +925,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 +945,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 +1007,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 +1427,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);