Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ptp: introduce programmable pins.

This patch adds a pair of new ioctls to the PTP Hardware Clock device
interface. Using the ioctls, user space programs can query each pin to
find out its current function and also reprogram a different function
if desired.

Signed-off-by: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Richard Cochran and committed by
David S. Miller
6092315d a85ae0e9

+226 -2
+127 -1
drivers/ptp/ptp_chardev.c
··· 25 25 26 26 #include "ptp_private.h" 27 27 28 + static int ptp_disable_pinfunc(struct ptp_clock_info *ops, 29 + enum ptp_pin_function func, unsigned int chan) 30 + { 31 + struct ptp_clock_request rq; 32 + int err = 0; 33 + 34 + memset(&rq, 0, sizeof(rq)); 35 + 36 + switch (func) { 37 + case PTP_PF_NONE: 38 + break; 39 + case PTP_PF_EXTTS: 40 + rq.type = PTP_CLK_REQ_EXTTS; 41 + rq.extts.index = chan; 42 + err = ops->enable(ops, &rq, 0); 43 + break; 44 + case PTP_PF_PEROUT: 45 + rq.type = PTP_CLK_REQ_PEROUT; 46 + rq.perout.index = chan; 47 + err = ops->enable(ops, &rq, 0); 48 + break; 49 + case PTP_PF_PHYSYNC: 50 + break; 51 + default: 52 + return -EINVAL; 53 + } 54 + 55 + return err; 56 + } 57 + 58 + int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin, 59 + enum ptp_pin_function func, unsigned int chan) 60 + { 61 + struct ptp_clock_info *info = ptp->info; 62 + struct ptp_pin_desc *pin1 = NULL, *pin2 = &info->pin_config[pin]; 63 + unsigned int i; 64 + 65 + /* Check to see if any other pin previously had this function. */ 66 + for (i = 0; i < info->n_pins; i++) { 67 + if (info->pin_config[i].func == func && 68 + info->pin_config[i].chan == chan) { 69 + pin1 = &info->pin_config[i]; 70 + break; 71 + } 72 + } 73 + if (pin1 && i == pin) 74 + return 0; 75 + 76 + /* Check the desired function and channel. */ 77 + switch (func) { 78 + case PTP_PF_NONE: 79 + break; 80 + case PTP_PF_EXTTS: 81 + if (chan >= info->n_ext_ts) 82 + return -EINVAL; 83 + break; 84 + case PTP_PF_PEROUT: 85 + if (chan >= info->n_per_out) 86 + return -EINVAL; 87 + break; 88 + case PTP_PF_PHYSYNC: 89 + pr_err("sorry, cannot reassign the calibration pin\n"); 90 + return -EINVAL; 91 + default: 92 + return -EINVAL; 93 + } 94 + 95 + if (pin2->func == PTP_PF_PHYSYNC) { 96 + pr_err("sorry, cannot reprogram the calibration pin\n"); 97 + return -EINVAL; 98 + } 99 + 100 + if (info->verify(info, pin, func, chan)) { 101 + pr_err("driver cannot use function %u on pin %u\n", func, chan); 102 + return -EOPNOTSUPP; 103 + } 104 + 105 + /* Disable whatever function was previously assigned. */ 106 + if (pin1) { 107 + ptp_disable_pinfunc(info, func, chan); 108 + pin1->func = PTP_PF_NONE; 109 + pin1->chan = 0; 110 + } 111 + ptp_disable_pinfunc(info, pin2->func, pin2->chan); 112 + pin2->func = func; 113 + pin2->chan = chan; 114 + 115 + return 0; 116 + } 117 + 28 118 int ptp_open(struct posix_clock *pc, fmode_t fmode) 29 119 { 30 120 return 0; ··· 125 35 struct ptp_clock_caps caps; 126 36 struct ptp_clock_request req; 127 37 struct ptp_sys_offset *sysoff = NULL; 38 + struct ptp_pin_desc pd; 128 39 struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); 129 40 struct ptp_clock_info *ops = ptp->info; 130 41 struct ptp_clock_time *pct; 131 42 struct timespec ts; 132 43 int enable, err = 0; 133 - unsigned int i; 44 + unsigned int i, pin_index; 134 45 135 46 switch (cmd) { 136 47 ··· 142 51 caps.n_ext_ts = ptp->info->n_ext_ts; 143 52 caps.n_per_out = ptp->info->n_per_out; 144 53 caps.pps = ptp->info->pps; 54 + caps.n_pins = ptp->info->n_pins; 145 55 if (copy_to_user((void __user *)arg, &caps, sizeof(caps))) 146 56 err = -EFAULT; 147 57 break; ··· 216 124 pct->nsec = ts.tv_nsec; 217 125 if (copy_to_user((void __user *)arg, sysoff, sizeof(*sysoff))) 218 126 err = -EFAULT; 127 + break; 128 + 129 + case PTP_PIN_GETFUNC: 130 + if (copy_from_user(&pd, (void __user *)arg, sizeof(pd))) { 131 + err = -EFAULT; 132 + break; 133 + } 134 + pin_index = pd.index; 135 + if (pin_index >= ops->n_pins) { 136 + err = -EINVAL; 137 + break; 138 + } 139 + if (mutex_lock_interruptible(&ptp->pincfg_mux)) 140 + return -ERESTARTSYS; 141 + pd = ops->pin_config[pin_index]; 142 + mutex_unlock(&ptp->pincfg_mux); 143 + if (!err && copy_to_user((void __user *)arg, &pd, sizeof(pd))) 144 + err = -EFAULT; 145 + break; 146 + 147 + case PTP_PIN_SETFUNC: 148 + if (copy_from_user(&pd, (void __user *)arg, sizeof(pd))) { 149 + err = -EFAULT; 150 + break; 151 + } 152 + pin_index = pd.index; 153 + if (pin_index >= ops->n_pins) { 154 + err = -EINVAL; 155 + break; 156 + } 157 + if (mutex_lock_interruptible(&ptp->pincfg_mux)) 158 + return -ERESTARTSYS; 159 + err = ptp_set_pinfunc(ptp, pin_index, pd.func, pd.chan); 160 + mutex_unlock(&ptp->pincfg_mux); 219 161 break; 220 162 221 163 default:
+23
drivers/ptp/ptp_clock.c
··· 169 169 struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); 170 170 171 171 mutex_destroy(&ptp->tsevq_mux); 172 + mutex_destroy(&ptp->pincfg_mux); 172 173 ida_simple_remove(&ptp_clocks_map, ptp->index); 173 174 kfree(ptp); 174 175 } ··· 204 203 ptp->index = index; 205 204 spin_lock_init(&ptp->tsevq.lock); 206 205 mutex_init(&ptp->tsevq_mux); 206 + mutex_init(&ptp->pincfg_mux); 207 207 init_waitqueue_head(&ptp->tsev_wq); 208 208 209 209 /* Create a new device in our class. */ ··· 251 249 device_destroy(ptp_class, ptp->devid); 252 250 no_device: 253 251 mutex_destroy(&ptp->tsevq_mux); 252 + mutex_destroy(&ptp->pincfg_mux); 254 253 no_slot: 255 254 kfree(ptp); 256 255 no_memory: ··· 307 304 return ptp->index; 308 305 } 309 306 EXPORT_SYMBOL(ptp_clock_index); 307 + 308 + int ptp_find_pin(struct ptp_clock *ptp, 309 + enum ptp_pin_function func, unsigned int chan) 310 + { 311 + struct ptp_pin_desc *pin = NULL; 312 + int i; 313 + 314 + mutex_lock(&ptp->pincfg_mux); 315 + for (i = 0; i < ptp->info->n_pins; i++) { 316 + if (ptp->info->pin_config[i].func == func && 317 + ptp->info->pin_config[i].chan == chan) { 318 + pin = &ptp->info->pin_config[i]; 319 + break; 320 + } 321 + } 322 + mutex_unlock(&ptp->pincfg_mux); 323 + 324 + return pin ? i : -1; 325 + } 326 + EXPORT_SYMBOL(ptp_find_pin); 310 327 311 328 /* module operations */ 312 329
+5
drivers/ptp/ptp_private.h
··· 48 48 long dialed_frequency; /* remembers the frequency adjustment */ 49 49 struct timestamp_event_queue tsevq; /* simple fifo for time stamps */ 50 50 struct mutex tsevq_mux; /* one process at a time reading the fifo */ 51 + struct mutex pincfg_mux; /* protect concurrent info->pin_config access */ 51 52 wait_queue_head_t tsev_wq; 52 53 int defunct; /* tells readers to go away when clock is being removed */ 53 54 }; ··· 69 68 /* 70 69 * see ptp_chardev.c 71 70 */ 71 + 72 + /* caller must hold pincfg_mux */ 73 + int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin, 74 + enum ptp_pin_function func, unsigned int chan); 72 75 73 76 long ptp_ioctl(struct posix_clock *pc, 74 77 unsigned int cmd, unsigned long arg);
+33
include/linux/ptp_clock_kernel.h
··· 49 49 * @n_alarm: The number of programmable alarms. 50 50 * @n_ext_ts: The number of external time stamp channels. 51 51 * @n_per_out: The number of programmable periodic signals. 52 + * @n_pins: The number of programmable pins. 52 53 * @pps: Indicates whether the clock supports a PPS callback. 54 + * @pin_config: Array of length 'n_pins'. If the number of 55 + * programmable pins is nonzero, then drivers must 56 + * allocate and initialize this array. 53 57 * 54 58 * clock operations 55 59 * ··· 74 70 * parameter request: Desired resource to enable or disable. 75 71 * parameter on: Caller passes one to enable or zero to disable. 76 72 * 73 + * @verify: Confirm that a pin can perform a given function. The PTP 74 + * Hardware Clock subsystem maintains the 'pin_config' 75 + * array on behalf of the drivers, but the PHC subsystem 76 + * assumes that every pin can perform every function. This 77 + * hook gives drivers a way of telling the core about 78 + * limitations on specific pins. This function must return 79 + * zero if the function can be assigned to this pin, and 80 + * nonzero otherwise. 81 + * parameter pin: index of the pin in question. 82 + * parameter func: the desired function to use. 83 + * parameter chan: the function channel index to use. 84 + * 77 85 * Drivers should embed their ptp_clock_info within a private 78 86 * structure, obtaining a reference to it using container_of(). 79 87 * ··· 99 83 int n_alarm; 100 84 int n_ext_ts; 101 85 int n_per_out; 86 + int n_pins; 102 87 int pps; 88 + struct ptp_pin_desc *pin_config; 103 89 int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta); 104 90 int (*adjtime)(struct ptp_clock_info *ptp, s64 delta); 105 91 int (*gettime)(struct ptp_clock_info *ptp, struct timespec *ts); 106 92 int (*settime)(struct ptp_clock_info *ptp, const struct timespec *ts); 107 93 int (*enable)(struct ptp_clock_info *ptp, 108 94 struct ptp_clock_request *request, int on); 95 + int (*verify)(struct ptp_clock_info *ptp, unsigned int pin, 96 + enum ptp_pin_function func, unsigned int chan); 109 97 }; 110 98 111 99 struct ptp_clock; ··· 175 155 */ 176 156 177 157 extern int ptp_clock_index(struct ptp_clock *ptp); 158 + 159 + /** 160 + * ptp_find_pin() - obtain the pin index of a given auxiliary function 161 + * 162 + * @ptp: The clock obtained from ptp_clock_register(). 163 + * @func: One of the ptp_pin_function enumerated values. 164 + * @chan: The particular functional channel to find. 165 + * Return: Pin index in the range of zero to ptp_clock_caps.n_pins - 1, 166 + * or -1 if the auxiliary function cannot be found. 167 + */ 168 + 169 + int ptp_find_pin(struct ptp_clock *ptp, 170 + enum ptp_pin_function func, unsigned int chan); 178 171 179 172 #endif
+38 -1
include/uapi/linux/ptp_clock.h
··· 50 50 int n_ext_ts; /* Number of external time stamp channels. */ 51 51 int n_per_out; /* Number of programmable periodic signals. */ 52 52 int pps; /* Whether the clock supports a PPS callback. */ 53 - int rsv[15]; /* Reserved for future use. */ 53 + int n_pins; /* Number of input/output pins. */ 54 + int rsv[14]; /* Reserved for future use. */ 54 55 }; 55 56 56 57 struct ptp_extts_request { ··· 81 80 struct ptp_clock_time ts[2 * PTP_MAX_SAMPLES + 1]; 82 81 }; 83 82 83 + enum ptp_pin_function { 84 + PTP_PF_NONE, 85 + PTP_PF_EXTTS, 86 + PTP_PF_PEROUT, 87 + PTP_PF_PHYSYNC, 88 + }; 89 + 90 + struct ptp_pin_desc { 91 + /* 92 + * Hardware specific human readable pin name. This field is 93 + * set by the kernel during the PTP_PIN_GETFUNC ioctl and is 94 + * ignored for the PTP_PIN_SETFUNC ioctl. 95 + */ 96 + char name[64]; 97 + /* 98 + * Pin index in the range of zero to ptp_clock_caps.n_pins - 1. 99 + */ 100 + unsigned int index; 101 + /* 102 + * Which of the PTP_PF_xxx functions to use on this pin. 103 + */ 104 + unsigned int func; 105 + /* 106 + * The specific channel to use for this function. 107 + * This corresponds to the 'index' field of the 108 + * PTP_EXTTS_REQUEST and PTP_PEROUT_REQUEST ioctls. 109 + */ 110 + unsigned int chan; 111 + /* 112 + * Reserved for future use. 113 + */ 114 + unsigned int rsv[5]; 115 + }; 116 + 84 117 #define PTP_CLK_MAGIC '=' 85 118 86 119 #define PTP_CLOCK_GETCAPS _IOR(PTP_CLK_MAGIC, 1, struct ptp_clock_caps) ··· 122 87 #define PTP_PEROUT_REQUEST _IOW(PTP_CLK_MAGIC, 3, struct ptp_perout_request) 123 88 #define PTP_ENABLE_PPS _IOW(PTP_CLK_MAGIC, 4, int) 124 89 #define PTP_SYS_OFFSET _IOW(PTP_CLK_MAGIC, 5, struct ptp_sys_offset) 90 + #define PTP_PIN_GETFUNC _IOWR(PTP_CLK_MAGIC, 6, struct ptp_pin_desc) 91 + #define PTP_PIN_SETFUNC _IOW(PTP_CLK_MAGIC, 7, struct ptp_pin_desc) 125 92 126 93 struct ptp_extts_event { 127 94 struct ptp_clock_time t; /* Time event occured. */