1*d69d5e83SAlexander Shishkin // SPDX-License-Identifier: GPL-2.0 2*d69d5e83SAlexander Shishkin /* 3*d69d5e83SAlexander Shishkin * MIPI SyS-T framing protocol for STM devices. 4*d69d5e83SAlexander Shishkin * Copyright (c) 2018, Intel Corporation. 5*d69d5e83SAlexander Shishkin */ 6*d69d5e83SAlexander Shishkin 7*d69d5e83SAlexander Shishkin #include <linux/configfs.h> 8*d69d5e83SAlexander Shishkin #include <linux/module.h> 9*d69d5e83SAlexander Shishkin #include <linux/device.h> 10*d69d5e83SAlexander Shishkin #include <linux/slab.h> 11*d69d5e83SAlexander Shishkin #include <linux/uuid.h> 12*d69d5e83SAlexander Shishkin #include <linux/stm.h> 13*d69d5e83SAlexander Shishkin #include "stm.h" 14*d69d5e83SAlexander Shishkin 15*d69d5e83SAlexander Shishkin enum sys_t_message_type { 16*d69d5e83SAlexander Shishkin MIPI_SYST_TYPE_BUILD = 0, 17*d69d5e83SAlexander Shishkin MIPI_SYST_TYPE_SHORT32, 18*d69d5e83SAlexander Shishkin MIPI_SYST_TYPE_STRING, 19*d69d5e83SAlexander Shishkin MIPI_SYST_TYPE_CATALOG, 20*d69d5e83SAlexander Shishkin MIPI_SYST_TYPE_RAW = 6, 21*d69d5e83SAlexander Shishkin MIPI_SYST_TYPE_SHORT64, 22*d69d5e83SAlexander Shishkin MIPI_SYST_TYPE_CLOCK, 23*d69d5e83SAlexander Shishkin }; 24*d69d5e83SAlexander Shishkin 25*d69d5e83SAlexander Shishkin enum sys_t_message_severity { 26*d69d5e83SAlexander Shishkin MIPI_SYST_SEVERITY_MAX = 0, 27*d69d5e83SAlexander Shishkin MIPI_SYST_SEVERITY_FATAL, 28*d69d5e83SAlexander Shishkin MIPI_SYST_SEVERITY_ERROR, 29*d69d5e83SAlexander Shishkin MIPI_SYST_SEVERITY_WARNING, 30*d69d5e83SAlexander Shishkin MIPI_SYST_SEVERITY_INFO, 31*d69d5e83SAlexander Shishkin MIPI_SYST_SEVERITY_USER1, 32*d69d5e83SAlexander Shishkin MIPI_SYST_SEVERITY_USER2, 33*d69d5e83SAlexander Shishkin MIPI_SYST_SEVERITY_DEBUG, 34*d69d5e83SAlexander Shishkin }; 35*d69d5e83SAlexander Shishkin 36*d69d5e83SAlexander Shishkin enum sys_t_message_build_subtype { 37*d69d5e83SAlexander Shishkin MIPI_SYST_BUILD_ID_COMPACT32 = 0, 38*d69d5e83SAlexander Shishkin MIPI_SYST_BUILD_ID_COMPACT64, 39*d69d5e83SAlexander Shishkin MIPI_SYST_BUILD_ID_LONG, 40*d69d5e83SAlexander Shishkin }; 41*d69d5e83SAlexander Shishkin 42*d69d5e83SAlexander Shishkin enum sys_t_message_clock_subtype { 43*d69d5e83SAlexander Shishkin MIPI_SYST_CLOCK_TRANSPORT_SYNC = 1, 44*d69d5e83SAlexander Shishkin }; 45*d69d5e83SAlexander Shishkin 46*d69d5e83SAlexander Shishkin enum sys_t_message_string_subtype { 47*d69d5e83SAlexander Shishkin MIPI_SYST_STRING_GENERIC = 1, 48*d69d5e83SAlexander Shishkin MIPI_SYST_STRING_FUNCTIONENTER, 49*d69d5e83SAlexander Shishkin MIPI_SYST_STRING_FUNCTIONEXIT, 50*d69d5e83SAlexander Shishkin MIPI_SYST_STRING_INVALIDPARAM = 5, 51*d69d5e83SAlexander Shishkin MIPI_SYST_STRING_ASSERT = 7, 52*d69d5e83SAlexander Shishkin MIPI_SYST_STRING_PRINTF_32 = 11, 53*d69d5e83SAlexander Shishkin MIPI_SYST_STRING_PRINTF_64 = 12, 54*d69d5e83SAlexander Shishkin }; 55*d69d5e83SAlexander Shishkin 56*d69d5e83SAlexander Shishkin #define MIPI_SYST_TYPE(t) ((u32)(MIPI_SYST_TYPE_ ## t)) 57*d69d5e83SAlexander Shishkin #define MIPI_SYST_SEVERITY(s) ((u32)(MIPI_SYST_SEVERITY_ ## s) << 4) 58*d69d5e83SAlexander Shishkin #define MIPI_SYST_OPT_LOC BIT(8) 59*d69d5e83SAlexander Shishkin #define MIPI_SYST_OPT_LEN BIT(9) 60*d69d5e83SAlexander Shishkin #define MIPI_SYST_OPT_CHK BIT(10) 61*d69d5e83SAlexander Shishkin #define MIPI_SYST_OPT_TS BIT(11) 62*d69d5e83SAlexander Shishkin #define MIPI_SYST_UNIT(u) ((u32)(u) << 12) 63*d69d5e83SAlexander Shishkin #define MIPI_SYST_ORIGIN(o) ((u32)(o) << 16) 64*d69d5e83SAlexander Shishkin #define MIPI_SYST_OPT_GUID BIT(23) 65*d69d5e83SAlexander Shishkin #define MIPI_SYST_SUBTYPE(s) ((u32)(MIPI_SYST_ ## s) << 24) 66*d69d5e83SAlexander Shishkin #define MIPI_SYST_UNITLARGE(u) (MIPI_SYST_UNIT(u & 0xf) | \ 67*d69d5e83SAlexander Shishkin MIPI_SYST_ORIGIN(u >> 4)) 68*d69d5e83SAlexander Shishkin #define MIPI_SYST_TYPES(t, s) (MIPI_SYST_TYPE(t) | \ 69*d69d5e83SAlexander Shishkin MIPI_SYST_SUBTYPE(t ## _ ## s)) 70*d69d5e83SAlexander Shishkin 71*d69d5e83SAlexander Shishkin #define DATA_HEADER (MIPI_SYST_TYPES(STRING, GENERIC) | \ 72*d69d5e83SAlexander Shishkin MIPI_SYST_SEVERITY(INFO) | \ 73*d69d5e83SAlexander Shishkin MIPI_SYST_OPT_GUID) 74*d69d5e83SAlexander Shishkin 75*d69d5e83SAlexander Shishkin struct sys_t_policy_node { 76*d69d5e83SAlexander Shishkin uuid_t uuid; 77*d69d5e83SAlexander Shishkin bool do_len; 78*d69d5e83SAlexander Shishkin unsigned long ts_interval; 79*d69d5e83SAlexander Shishkin }; 80*d69d5e83SAlexander Shishkin 81*d69d5e83SAlexander Shishkin struct sys_t_output { 82*d69d5e83SAlexander Shishkin struct sys_t_policy_node node; 83*d69d5e83SAlexander Shishkin unsigned long ts_jiffies; 84*d69d5e83SAlexander Shishkin }; 85*d69d5e83SAlexander Shishkin 86*d69d5e83SAlexander Shishkin static void sys_t_policy_node_init(void *priv) 87*d69d5e83SAlexander Shishkin { 88*d69d5e83SAlexander Shishkin struct sys_t_policy_node *pn = priv; 89*d69d5e83SAlexander Shishkin 90*d69d5e83SAlexander Shishkin generate_random_uuid(pn->uuid.b); 91*d69d5e83SAlexander Shishkin } 92*d69d5e83SAlexander Shishkin 93*d69d5e83SAlexander Shishkin static int sys_t_output_open(void *priv, struct stm_output *output) 94*d69d5e83SAlexander Shishkin { 95*d69d5e83SAlexander Shishkin struct sys_t_policy_node *pn = priv; 96*d69d5e83SAlexander Shishkin struct sys_t_output *opriv; 97*d69d5e83SAlexander Shishkin 98*d69d5e83SAlexander Shishkin opriv = kzalloc(sizeof(*opriv), GFP_ATOMIC); 99*d69d5e83SAlexander Shishkin if (!opriv) 100*d69d5e83SAlexander Shishkin return -ENOMEM; 101*d69d5e83SAlexander Shishkin 102*d69d5e83SAlexander Shishkin memcpy(&opriv->node, pn, sizeof(opriv->node)); 103*d69d5e83SAlexander Shishkin output->pdrv_private = opriv; 104*d69d5e83SAlexander Shishkin 105*d69d5e83SAlexander Shishkin return 0; 106*d69d5e83SAlexander Shishkin } 107*d69d5e83SAlexander Shishkin 108*d69d5e83SAlexander Shishkin static void sys_t_output_close(struct stm_output *output) 109*d69d5e83SAlexander Shishkin { 110*d69d5e83SAlexander Shishkin kfree(output->pdrv_private); 111*d69d5e83SAlexander Shishkin } 112*d69d5e83SAlexander Shishkin 113*d69d5e83SAlexander Shishkin static ssize_t sys_t_policy_uuid_show(struct config_item *item, 114*d69d5e83SAlexander Shishkin char *page) 115*d69d5e83SAlexander Shishkin { 116*d69d5e83SAlexander Shishkin struct sys_t_policy_node *pn = to_pdrv_policy_node(item); 117*d69d5e83SAlexander Shishkin 118*d69d5e83SAlexander Shishkin return sprintf(page, "%pU\n", &pn->uuid); 119*d69d5e83SAlexander Shishkin } 120*d69d5e83SAlexander Shishkin 121*d69d5e83SAlexander Shishkin static ssize_t 122*d69d5e83SAlexander Shishkin sys_t_policy_uuid_store(struct config_item *item, const char *page, 123*d69d5e83SAlexander Shishkin size_t count) 124*d69d5e83SAlexander Shishkin { 125*d69d5e83SAlexander Shishkin struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex; 126*d69d5e83SAlexander Shishkin struct sys_t_policy_node *pn = to_pdrv_policy_node(item); 127*d69d5e83SAlexander Shishkin int ret; 128*d69d5e83SAlexander Shishkin 129*d69d5e83SAlexander Shishkin mutex_lock(mutexp); 130*d69d5e83SAlexander Shishkin ret = uuid_parse(page, &pn->uuid); 131*d69d5e83SAlexander Shishkin mutex_unlock(mutexp); 132*d69d5e83SAlexander Shishkin 133*d69d5e83SAlexander Shishkin return ret < 0 ? ret : count; 134*d69d5e83SAlexander Shishkin } 135*d69d5e83SAlexander Shishkin 136*d69d5e83SAlexander Shishkin CONFIGFS_ATTR(sys_t_policy_, uuid); 137*d69d5e83SAlexander Shishkin 138*d69d5e83SAlexander Shishkin static ssize_t sys_t_policy_do_len_show(struct config_item *item, 139*d69d5e83SAlexander Shishkin char *page) 140*d69d5e83SAlexander Shishkin { 141*d69d5e83SAlexander Shishkin struct sys_t_policy_node *pn = to_pdrv_policy_node(item); 142*d69d5e83SAlexander Shishkin 143*d69d5e83SAlexander Shishkin return sprintf(page, "%d\n", pn->do_len); 144*d69d5e83SAlexander Shishkin } 145*d69d5e83SAlexander Shishkin 146*d69d5e83SAlexander Shishkin static ssize_t 147*d69d5e83SAlexander Shishkin sys_t_policy_do_len_store(struct config_item *item, const char *page, 148*d69d5e83SAlexander Shishkin size_t count) 149*d69d5e83SAlexander Shishkin { 150*d69d5e83SAlexander Shishkin struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex; 151*d69d5e83SAlexander Shishkin struct sys_t_policy_node *pn = to_pdrv_policy_node(item); 152*d69d5e83SAlexander Shishkin int ret; 153*d69d5e83SAlexander Shishkin 154*d69d5e83SAlexander Shishkin mutex_lock(mutexp); 155*d69d5e83SAlexander Shishkin ret = kstrtobool(page, &pn->do_len); 156*d69d5e83SAlexander Shishkin mutex_unlock(mutexp); 157*d69d5e83SAlexander Shishkin 158*d69d5e83SAlexander Shishkin return ret ? ret : count; 159*d69d5e83SAlexander Shishkin } 160*d69d5e83SAlexander Shishkin 161*d69d5e83SAlexander Shishkin CONFIGFS_ATTR(sys_t_policy_, do_len); 162*d69d5e83SAlexander Shishkin 163*d69d5e83SAlexander Shishkin static ssize_t sys_t_policy_ts_interval_show(struct config_item *item, 164*d69d5e83SAlexander Shishkin char *page) 165*d69d5e83SAlexander Shishkin { 166*d69d5e83SAlexander Shishkin struct sys_t_policy_node *pn = to_pdrv_policy_node(item); 167*d69d5e83SAlexander Shishkin 168*d69d5e83SAlexander Shishkin return sprintf(page, "%u\n", jiffies_to_msecs(pn->ts_interval)); 169*d69d5e83SAlexander Shishkin } 170*d69d5e83SAlexander Shishkin 171*d69d5e83SAlexander Shishkin static ssize_t 172*d69d5e83SAlexander Shishkin sys_t_policy_ts_interval_store(struct config_item *item, const char *page, 173*d69d5e83SAlexander Shishkin size_t count) 174*d69d5e83SAlexander Shishkin { 175*d69d5e83SAlexander Shishkin struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex; 176*d69d5e83SAlexander Shishkin struct sys_t_policy_node *pn = to_pdrv_policy_node(item); 177*d69d5e83SAlexander Shishkin unsigned int ms; 178*d69d5e83SAlexander Shishkin int ret; 179*d69d5e83SAlexander Shishkin 180*d69d5e83SAlexander Shishkin mutex_lock(mutexp); 181*d69d5e83SAlexander Shishkin ret = kstrtouint(page, 10, &ms); 182*d69d5e83SAlexander Shishkin mutex_unlock(mutexp); 183*d69d5e83SAlexander Shishkin 184*d69d5e83SAlexander Shishkin if (!ret) { 185*d69d5e83SAlexander Shishkin pn->ts_interval = msecs_to_jiffies(ms); 186*d69d5e83SAlexander Shishkin return count; 187*d69d5e83SAlexander Shishkin } 188*d69d5e83SAlexander Shishkin 189*d69d5e83SAlexander Shishkin return ret; 190*d69d5e83SAlexander Shishkin } 191*d69d5e83SAlexander Shishkin 192*d69d5e83SAlexander Shishkin CONFIGFS_ATTR(sys_t_policy_, ts_interval); 193*d69d5e83SAlexander Shishkin 194*d69d5e83SAlexander Shishkin static struct configfs_attribute *sys_t_policy_attrs[] = { 195*d69d5e83SAlexander Shishkin &sys_t_policy_attr_uuid, 196*d69d5e83SAlexander Shishkin &sys_t_policy_attr_do_len, 197*d69d5e83SAlexander Shishkin &sys_t_policy_attr_ts_interval, 198*d69d5e83SAlexander Shishkin NULL, 199*d69d5e83SAlexander Shishkin }; 200*d69d5e83SAlexander Shishkin 201*d69d5e83SAlexander Shishkin static inline bool sys_t_need_ts(struct sys_t_output *op) 202*d69d5e83SAlexander Shishkin { 203*d69d5e83SAlexander Shishkin if (op->node.ts_interval && 204*d69d5e83SAlexander Shishkin time_after(op->ts_jiffies + op->node.ts_interval, jiffies)) { 205*d69d5e83SAlexander Shishkin op->ts_jiffies = jiffies; 206*d69d5e83SAlexander Shishkin 207*d69d5e83SAlexander Shishkin return true; 208*d69d5e83SAlexander Shishkin } 209*d69d5e83SAlexander Shishkin 210*d69d5e83SAlexander Shishkin return false; 211*d69d5e83SAlexander Shishkin } 212*d69d5e83SAlexander Shishkin 213*d69d5e83SAlexander Shishkin static ssize_t sys_t_write(struct stm_data *data, struct stm_output *output, 214*d69d5e83SAlexander Shishkin unsigned int chan, const char *buf, size_t count) 215*d69d5e83SAlexander Shishkin { 216*d69d5e83SAlexander Shishkin struct sys_t_output *op = output->pdrv_private; 217*d69d5e83SAlexander Shishkin unsigned int c = output->channel + chan; 218*d69d5e83SAlexander Shishkin unsigned int m = output->master; 219*d69d5e83SAlexander Shishkin const unsigned char nil = 0; 220*d69d5e83SAlexander Shishkin u32 header = DATA_HEADER; 221*d69d5e83SAlexander Shishkin ssize_t sz; 222*d69d5e83SAlexander Shishkin 223*d69d5e83SAlexander Shishkin /* We require an existing policy node to proceed */ 224*d69d5e83SAlexander Shishkin if (!op) 225*d69d5e83SAlexander Shishkin return -EINVAL; 226*d69d5e83SAlexander Shishkin 227*d69d5e83SAlexander Shishkin if (op->node.do_len) 228*d69d5e83SAlexander Shishkin header |= MIPI_SYST_OPT_LEN; 229*d69d5e83SAlexander Shishkin if (sys_t_need_ts(op)) 230*d69d5e83SAlexander Shishkin header |= MIPI_SYST_OPT_TS; 231*d69d5e83SAlexander Shishkin 232*d69d5e83SAlexander Shishkin /* 233*d69d5e83SAlexander Shishkin * STP framing rules for SyS-T frames: 234*d69d5e83SAlexander Shishkin * * the first packet of the SyS-T frame is timestamped; 235*d69d5e83SAlexander Shishkin * * the last packet is a FLAG. 236*d69d5e83SAlexander Shishkin */ 237*d69d5e83SAlexander Shishkin /* Message layout: HEADER / GUID / [LENGTH /][TIMESTAMP /] DATA */ 238*d69d5e83SAlexander Shishkin /* HEADER */ 239*d69d5e83SAlexander Shishkin sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED, 240*d69d5e83SAlexander Shishkin 4, (u8 *)&header); 241*d69d5e83SAlexander Shishkin if (sz <= 0) 242*d69d5e83SAlexander Shishkin return sz; 243*d69d5e83SAlexander Shishkin 244*d69d5e83SAlexander Shishkin /* GUID */ 245*d69d5e83SAlexander Shishkin sz = stm_data_write(data, m, c, false, op->node.uuid.b, UUID_SIZE); 246*d69d5e83SAlexander Shishkin if (sz <= 0) 247*d69d5e83SAlexander Shishkin return sz; 248*d69d5e83SAlexander Shishkin 249*d69d5e83SAlexander Shishkin /* [LENGTH] */ 250*d69d5e83SAlexander Shishkin if (op->node.do_len) { 251*d69d5e83SAlexander Shishkin u16 length = count; 252*d69d5e83SAlexander Shishkin 253*d69d5e83SAlexander Shishkin sz = data->packet(data, m, c, STP_PACKET_DATA, 0, 2, 254*d69d5e83SAlexander Shishkin (u8 *)&length); 255*d69d5e83SAlexander Shishkin if (sz <= 0) 256*d69d5e83SAlexander Shishkin return sz; 257*d69d5e83SAlexander Shishkin } 258*d69d5e83SAlexander Shishkin 259*d69d5e83SAlexander Shishkin /* [TIMESTAMP] */ 260*d69d5e83SAlexander Shishkin if (header & MIPI_SYST_OPT_TS) { 261*d69d5e83SAlexander Shishkin u64 ts = ktime_get_real_ns(); 262*d69d5e83SAlexander Shishkin 263*d69d5e83SAlexander Shishkin sz = stm_data_write(data, m, c, false, &ts, sizeof(ts)); 264*d69d5e83SAlexander Shishkin if (sz <= 0) 265*d69d5e83SAlexander Shishkin return sz; 266*d69d5e83SAlexander Shishkin } 267*d69d5e83SAlexander Shishkin 268*d69d5e83SAlexander Shishkin /* DATA */ 269*d69d5e83SAlexander Shishkin sz = stm_data_write(data, m, c, false, buf, count); 270*d69d5e83SAlexander Shishkin if (sz > 0) 271*d69d5e83SAlexander Shishkin data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil); 272*d69d5e83SAlexander Shishkin 273*d69d5e83SAlexander Shishkin return sz; 274*d69d5e83SAlexander Shishkin } 275*d69d5e83SAlexander Shishkin 276*d69d5e83SAlexander Shishkin static const struct stm_protocol_driver sys_t_pdrv = { 277*d69d5e83SAlexander Shishkin .owner = THIS_MODULE, 278*d69d5e83SAlexander Shishkin .name = "p_sys-t", 279*d69d5e83SAlexander Shishkin .priv_sz = sizeof(struct sys_t_policy_node), 280*d69d5e83SAlexander Shishkin .write = sys_t_write, 281*d69d5e83SAlexander Shishkin .policy_attr = sys_t_policy_attrs, 282*d69d5e83SAlexander Shishkin .policy_node_init = sys_t_policy_node_init, 283*d69d5e83SAlexander Shishkin .output_open = sys_t_output_open, 284*d69d5e83SAlexander Shishkin .output_close = sys_t_output_close, 285*d69d5e83SAlexander Shishkin }; 286*d69d5e83SAlexander Shishkin 287*d69d5e83SAlexander Shishkin static int sys_t_stm_init(void) 288*d69d5e83SAlexander Shishkin { 289*d69d5e83SAlexander Shishkin return stm_register_protocol(&sys_t_pdrv); 290*d69d5e83SAlexander Shishkin } 291*d69d5e83SAlexander Shishkin 292*d69d5e83SAlexander Shishkin static void sys_t_stm_exit(void) 293*d69d5e83SAlexander Shishkin { 294*d69d5e83SAlexander Shishkin stm_unregister_protocol(&sys_t_pdrv); 295*d69d5e83SAlexander Shishkin } 296*d69d5e83SAlexander Shishkin 297*d69d5e83SAlexander Shishkin module_init(sys_t_stm_init); 298*d69d5e83SAlexander Shishkin module_exit(sys_t_stm_exit); 299*d69d5e83SAlexander Shishkin 300*d69d5e83SAlexander Shishkin MODULE_LICENSE("GPL v2"); 301*d69d5e83SAlexander Shishkin MODULE_DESCRIPTION("MIPI SyS-T STM framing protocol driver"); 302*d69d5e83SAlexander Shishkin MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>"); 303