Lines Matching +full:bt1 +full:- +full:pvt

1 // SPDX-License-Identifier: GPL-2.0-only
9 * Baikal-T1 Process, Voltage, Temperature sensor driver
18 #include <linux/hwmon-sysfs.h>
33 #include "bt1-pvt.h"
43 PVT_SENSOR_INFO(1, "CPU Core Low-Vt", hwmon_in, LVT, LTHRES),
44 PVT_SENSOR_INFO(2, "CPU Core High-Vt", hwmon_in, HVT, HTHRES),
45 PVT_SENSOR_INFO(3, "CPU Core Standard-Vt", hwmon_in, SVT, STHRES),
50 * to PVT data and vice-versa are following:
51 * N = 1.8322e-8*(T^4) + 2.343e-5*(T^3) + 8.7018e-3*(T^2) + 3.9269*(T^1) +
53 * T = -1.6743e-11*(N^4) + 8.1542e-8*(N^3) + -1.8201e-4*(N^2) +
54 * 3.1020e-1*(N^1) - 4.838e1,
55 * where T = [-48.380, 147.438]C and N = [0, 1023].
62 * N = (18322e-20*(T^4) + 2343e-13*(T^3) + 87018e-9*(T^2) + 39269e-3*T +
64 * T = -16743e-12*(D^4) + 81542e-9*(D^3) - 182010e-6*(D^2) + 310200e-3*D -
66 * where T = [-48380, 147438] mC and N = [0, 1023].
82 {4, -16743, 1000, 1},
84 {2, -182010, 1000, 1},
86 {0, -48380, 1, 1}
93 * N = 1.8658e3*V - 1.1572e3,
97 * N = (18658e-3*V - 11572) / 10,
104 {0, -11572, 1, 1}
127 const struct pvt_poly_term *term = poly->terms; in pvt_calc_poly()
132 tmp = term->coef; in pvt_calc_poly()
133 for (deg = 0; deg < term->deg; ++deg) in pvt_calc_poly()
134 tmp = mult_frac(tmp, data, term->divider); in pvt_calc_poly()
135 ret += tmp / term->divider_leftover; in pvt_calc_poly()
136 } while ((term++)->deg); in pvt_calc_poly()
138 return ret / poly->total_divider; in pvt_calc_poly()
152 * Baikal-T1 PVT mode can be updated only when the controller is disabled.
158 static inline void pvt_set_mode(struct pvt_hwmon *pvt, u32 mode) in pvt_set_mode() argument
164 old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); in pvt_set_mode()
165 pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_MODE_MASK | PVT_CTRL_EN, in pvt_set_mode()
176 static inline void pvt_set_trim(struct pvt_hwmon *pvt, u32 trim) in pvt_set_trim() argument
182 old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); in pvt_set_trim()
183 pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_TRIM_MASK | PVT_CTRL_EN, in pvt_set_trim()
187 static inline void pvt_set_tout(struct pvt_hwmon *pvt, u32 tout) in pvt_set_tout() argument
191 old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); in pvt_set_tout()
192 writel(tout, pvt->regs + PVT_TTIMEOUT); in pvt_set_tout()
193 pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, old); in pvt_set_tout()
197 * This driver can optionally provide the hwmon alarms for each sensor the PVT
198 * controller supports. The alarms functionality is made compile-time
201 * your system design it's recommended to have them disabled to prevent the PVT
205 * Baikal-T1 PVT embedded controller is based on the Analog Bits PVT sensor,
206 * but is equipped with a dedicated control wrapper. It exposes the PVT
207 * sub-block registers space via the APB3 bus. In addition the wrapper provides
231 struct pvt_hwmon *pvt = data; in pvt_soft_isr() local
240 thres_sts = readl(pvt->regs + PVT_RAW_INTR_STAT); in pvt_soft_isr()
243 * Then lets recharge the PVT interface with the next sampling mode. in pvt_soft_isr()
247 cache = &pvt->cache[pvt->sensor]; in pvt_soft_isr()
248 info = &pvt_info[pvt->sensor]; in pvt_soft_isr()
249 pvt->sensor = (pvt->sensor == PVT_SENSOR_LAST) ? in pvt_soft_isr()
250 PVT_SENSOR_FIRST : (pvt->sensor + 1); in pvt_soft_isr()
261 mutex_lock(&pvt->iface_mtx); in pvt_soft_isr()
263 old = pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, in pvt_soft_isr()
266 val = readl(pvt->regs + PVT_DATA); in pvt_soft_isr()
268 pvt_set_mode(pvt, pvt_info[pvt->sensor].mode); in pvt_soft_isr()
270 pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, old); in pvt_soft_isr()
272 mutex_unlock(&pvt->iface_mtx); in pvt_soft_isr()
276 * sensor. Lock write-seqlock to make sure the reader has a coherent in pvt_soft_isr()
279 write_seqlock(&cache->data_seqlock); in pvt_soft_isr()
281 cache->data = FIELD_GET(PVT_DATA_DATA_MASK, val); in pvt_soft_isr()
283 write_sequnlock(&cache->data_seqlock); in pvt_soft_isr()
286 * While PVT core is doing the next mode data conversion, we'll check in pvt_soft_isr()
289 * set at a time, that's why if-else statement is utilized. in pvt_soft_isr()
291 if ((thres_sts & info->thres_sts_lo) ^ cache->thres_sts_lo) { in pvt_soft_isr()
292 WRITE_ONCE(cache->thres_sts_lo, thres_sts & info->thres_sts_lo); in pvt_soft_isr()
293 hwmon_notify_event(pvt->hwmon, info->type, info->attr_min_alarm, in pvt_soft_isr()
294 info->channel); in pvt_soft_isr()
295 } else if ((thres_sts & info->thres_sts_hi) ^ cache->thres_sts_hi) { in pvt_soft_isr()
296 WRITE_ONCE(cache->thres_sts_hi, thres_sts & info->thres_sts_hi); in pvt_soft_isr()
297 hwmon_notify_event(pvt->hwmon, info->type, info->attr_max_alarm, in pvt_soft_isr()
298 info->channel); in pvt_soft_isr()
314 static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type, in pvt_read_data() argument
317 struct pvt_cache *cache = &pvt->cache[type]; in pvt_read_data()
322 seq = read_seqbegin(&cache->data_seqlock); in pvt_read_data()
323 data = cache->data; in pvt_read_data()
324 } while (read_seqretry(&cache->data_seqlock, seq)); in pvt_read_data()
334 static int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type, in pvt_read_limit() argument
340 data = readl(pvt->regs + pvt_info[type].thres_base); in pvt_read_limit()
355 static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type, in pvt_write_limit() argument
370 ret = mutex_lock_interruptible(&pvt->iface_mtx); in pvt_write_limit()
375 limit = readl(pvt->regs + pvt_info[type].thres_base); in pvt_write_limit()
388 pvt_update(pvt->regs + pvt_info[type].thres_base, mask, data); in pvt_write_limit()
390 mutex_unlock(&pvt->iface_mtx); in pvt_write_limit()
395 static int pvt_read_alarm(struct pvt_hwmon *pvt, enum pvt_sensor_type type, in pvt_read_alarm() argument
399 *val = !!READ_ONCE(pvt->cache[type].thres_sts_lo); in pvt_read_alarm()
401 *val = !!READ_ONCE(pvt->cache[type].thres_sts_hi); in pvt_read_alarm()
434 struct pvt_hwmon *pvt = data; in pvt_hard_isr() local
442 pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, in pvt_hard_isr()
446 * Nothing special for alarm-less driver. Just read the data, update in pvt_hard_isr()
449 val = readl(pvt->regs + PVT_DATA); in pvt_hard_isr()
451 dev_err(pvt->dev, "Got IRQ when data isn't valid\n"); in pvt_hard_isr()
455 cache = &pvt->cache[pvt->sensor]; in pvt_hard_isr()
457 WRITE_ONCE(cache->data, FIELD_GET(PVT_DATA_DATA_MASK, val)); in pvt_hard_isr()
459 complete(&cache->conversion); in pvt_hard_isr()
476 static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type, in pvt_read_data() argument
479 struct pvt_cache *cache = &pvt->cache[type]; in pvt_read_data()
485 * Lock PVT conversion interface until data cache is updated. The in pvt_read_data()
486 * data read procedure is following: set the requested PVT sensor in pvt_read_data()
490 ret = mutex_lock_interruptible(&pvt->iface_mtx); in pvt_read_data()
494 pvt->sensor = type; in pvt_read_data()
495 pvt_set_mode(pvt, pvt_info[type].mode); in pvt_read_data()
501 pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0); in pvt_read_data()
502 pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN); in pvt_read_data()
510 timeout = 2 * usecs_to_jiffies(ktime_to_us(pvt->timeout)); in pvt_read_data()
511 ret = wait_for_completion_timeout(&cache->conversion, timeout); in pvt_read_data()
513 pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); in pvt_read_data()
514 pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, in pvt_read_data()
517 data = READ_ONCE(cache->data); in pvt_read_data()
519 mutex_unlock(&pvt->iface_mtx); in pvt_read_data()
522 return -ETIMEDOUT; in pvt_read_data()
532 static int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type, in pvt_read_limit() argument
535 return -EOPNOTSUPP; in pvt_read_limit()
538 static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type, in pvt_write_limit() argument
541 return -EOPNOTSUPP; in pvt_write_limit()
544 static int pvt_read_alarm(struct pvt_hwmon *pvt, enum pvt_sensor_type type, in pvt_read_alarm() argument
547 return -EOPNOTSUPP; in pvt_read_alarm()
636 static int pvt_read_trim(struct pvt_hwmon *pvt, long *val) in pvt_read_trim() argument
640 data = readl(pvt->regs + PVT_CTRL); in pvt_read_trim()
646 static int pvt_write_trim(struct pvt_hwmon *pvt, long val) in pvt_write_trim() argument
655 ret = mutex_lock_interruptible(&pvt->iface_mtx); in pvt_write_trim()
660 pvt_set_trim(pvt, trim); in pvt_write_trim()
662 mutex_unlock(&pvt->iface_mtx); in pvt_write_trim()
667 static int pvt_read_timeout(struct pvt_hwmon *pvt, long *val) in pvt_read_timeout() argument
671 ret = mutex_lock_interruptible(&pvt->iface_mtx); in pvt_read_timeout()
676 *val = ktime_to_ms(pvt->timeout); in pvt_read_timeout()
678 mutex_unlock(&pvt->iface_mtx); in pvt_read_timeout()
683 static int pvt_write_timeout(struct pvt_hwmon *pvt, long val) in pvt_write_timeout() argument
690 rate = clk_get_rate(pvt->clks[PVT_CLOCK_REF].clk); in pvt_write_timeout()
692 return -ENODEV; in pvt_write_timeout()
706 * PVT sampling rate. Make sure the timeout is not negative. in pvt_write_timeout()
720 * we have to disable PVT in order to have the new delay actually in pvt_write_timeout()
723 ret = mutex_lock_interruptible(&pvt->iface_mtx); in pvt_write_timeout()
727 pvt_set_tout(pvt, data); in pvt_write_timeout()
728 pvt->timeout = cache; in pvt_write_timeout()
730 mutex_unlock(&pvt->iface_mtx); in pvt_write_timeout()
738 struct pvt_hwmon *pvt = dev_get_drvdata(dev); in pvt_hwmon_read() local
741 return -EINVAL; in pvt_hwmon_read()
747 return pvt_read_timeout(pvt, val); in pvt_hwmon_read()
753 return pvt_read_data(pvt, ch, val); in pvt_hwmon_read()
758 return pvt_read_limit(pvt, ch, true, val); in pvt_hwmon_read()
760 return pvt_read_limit(pvt, ch, false, val); in pvt_hwmon_read()
762 return pvt_read_alarm(pvt, ch, true, val); in pvt_hwmon_read()
764 return pvt_read_alarm(pvt, ch, false, val); in pvt_hwmon_read()
766 return pvt_read_trim(pvt, val); in pvt_hwmon_read()
772 return pvt_read_data(pvt, PVT_VOLT + ch, val); in pvt_hwmon_read()
774 return pvt_read_limit(pvt, PVT_VOLT + ch, true, val); in pvt_hwmon_read()
776 return pvt_read_limit(pvt, PVT_VOLT + ch, false, val); in pvt_hwmon_read()
778 return pvt_read_alarm(pvt, PVT_VOLT + ch, true, val); in pvt_hwmon_read()
780 return pvt_read_alarm(pvt, PVT_VOLT + ch, false, val); in pvt_hwmon_read()
787 return -EOPNOTSUPP; in pvt_hwmon_read()
795 return -EINVAL; in pvt_hwmon_read_string()
816 return -EOPNOTSUPP; in pvt_hwmon_read_string()
822 struct pvt_hwmon *pvt = dev_get_drvdata(dev); in pvt_hwmon_write() local
825 return -EINVAL; in pvt_hwmon_write()
831 return pvt_write_timeout(pvt, val); in pvt_hwmon_write()
837 return pvt_write_limit(pvt, ch, true, val); in pvt_hwmon_write()
839 return pvt_write_limit(pvt, ch, false, val); in pvt_hwmon_write()
841 return pvt_write_trim(pvt, val); in pvt_hwmon_write()
847 return pvt_write_limit(pvt, PVT_VOLT + ch, true, val); in pvt_hwmon_write()
849 return pvt_write_limit(pvt, PVT_VOLT + ch, false, val); in pvt_hwmon_write()
856 return -EOPNOTSUPP; in pvt_hwmon_write()
873 struct pvt_hwmon *pvt = data; in pvt_clear_data() local
878 complete_all(&pvt->cache[idx].conversion); in pvt_clear_data()
881 mutex_destroy(&pvt->iface_mtx); in pvt_clear_data()
886 struct device *dev = &pdev->dev; in pvt_create_data()
887 struct pvt_hwmon *pvt; in pvt_create_data() local
890 pvt = devm_kzalloc(dev, sizeof(*pvt), GFP_KERNEL); in pvt_create_data()
891 if (!pvt) in pvt_create_data()
892 return ERR_PTR(-ENOMEM); in pvt_create_data()
894 ret = devm_add_action(dev, pvt_clear_data, pvt); in pvt_create_data()
896 dev_err(dev, "Can't add PVT data clear action\n"); in pvt_create_data()
900 pvt->dev = dev; in pvt_create_data()
901 pvt->sensor = PVT_SENSOR_FIRST; in pvt_create_data()
902 mutex_init(&pvt->iface_mtx); in pvt_create_data()
906 seqlock_init(&pvt->cache[idx].data_seqlock); in pvt_create_data()
909 init_completion(&pvt->cache[idx].conversion); in pvt_create_data()
912 return pvt; in pvt_create_data()
915 static int pvt_request_regs(struct pvt_hwmon *pvt) in pvt_request_regs() argument
917 struct platform_device *pdev = to_platform_device(pvt->dev); in pvt_request_regs()
922 dev_err(pvt->dev, "Couldn't find PVT memresource\n"); in pvt_request_regs()
923 return -EINVAL; in pvt_request_regs()
926 pvt->regs = devm_ioremap_resource(pvt->dev, res); in pvt_request_regs()
927 if (IS_ERR(pvt->regs)) { in pvt_request_regs()
928 dev_err(pvt->dev, "Couldn't map PVT registers\n"); in pvt_request_regs()
929 return PTR_ERR(pvt->regs); in pvt_request_regs()
937 struct pvt_hwmon *pvt = data; in pvt_disable_clks() local
939 clk_bulk_disable_unprepare(PVT_CLOCK_NUM, pvt->clks); in pvt_disable_clks()
942 static int pvt_request_clks(struct pvt_hwmon *pvt) in pvt_request_clks() argument
946 pvt->clks[PVT_CLOCK_APB].id = "pclk"; in pvt_request_clks()
947 pvt->clks[PVT_CLOCK_REF].id = "ref"; in pvt_request_clks()
949 ret = devm_clk_bulk_get(pvt->dev, PVT_CLOCK_NUM, pvt->clks); in pvt_request_clks()
951 dev_err(pvt->dev, "Couldn't get PVT clocks descriptors\n"); in pvt_request_clks()
955 ret = clk_bulk_prepare_enable(PVT_CLOCK_NUM, pvt->clks); in pvt_request_clks()
957 dev_err(pvt->dev, "Couldn't enable the PVT clocks\n"); in pvt_request_clks()
961 ret = devm_add_action_or_reset(pvt->dev, pvt_disable_clks, pvt); in pvt_request_clks()
963 dev_err(pvt->dev, "Can't add PVT clocks disable action\n"); in pvt_request_clks()
970 static int pvt_check_pwr(struct pvt_hwmon *pvt) in pvt_check_pwr() argument
981 * data read procedure will either return -ETIMEDOUT (for the in pvt_check_pwr()
982 * alarm-less driver configuration) or just stop the repeated in pvt_check_pwr()
986 pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL); in pvt_check_pwr()
987 pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN); in pvt_check_pwr()
988 pvt_set_tout(pvt, 0); in pvt_check_pwr()
989 readl(pvt->regs + PVT_DATA); in pvt_check_pwr()
994 data = readl(pvt->regs + PVT_DATA); in pvt_check_pwr()
996 ret = -ENODEV; in pvt_check_pwr()
997 dev_err(pvt->dev, "Sensor is powered down\n"); in pvt_check_pwr()
1000 pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); in pvt_check_pwr()
1005 static int pvt_init_iface(struct pvt_hwmon *pvt) in pvt_init_iface() argument
1010 rate = clk_get_rate(pvt->clks[PVT_CLOCK_REF].clk); in pvt_init_iface()
1012 dev_err(pvt->dev, "Invalid reference clock rate\n"); in pvt_init_iface()
1013 return -ENODEV; in pvt_init_iface()
1021 pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL); in pvt_init_iface()
1022 pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); in pvt_init_iface()
1023 readl(pvt->regs + PVT_CLR_INTR); in pvt_init_iface()
1024 readl(pvt->regs + PVT_DATA); in pvt_init_iface()
1027 pvt_set_mode(pvt, pvt_info[pvt->sensor].mode); in pvt_init_iface()
1028 pvt_set_tout(pvt, PVT_TOUT_DEF); in pvt_init_iface()
1031 * Preserve the current ref-clock based delay (Ttotal) between the in pvt_init_iface()
1034 * delay introduced by the internal ref-clock timer (N / Fclk) and the in pvt_init_iface()
1044 pvt->timeout = ktime_set(PVT_SENSORS_NUM * PVT_TOUT_DEF, 0); in pvt_init_iface()
1045 pvt->timeout = ktime_divns(pvt->timeout, rate); in pvt_init_iface()
1046 pvt->timeout = ktime_add_ns(pvt->timeout, PVT_SENSORS_NUM * PVT_TOUT_MIN); in pvt_init_iface()
1048 pvt->timeout = ktime_set(PVT_TOUT_DEF, 0); in pvt_init_iface()
1049 pvt->timeout = ktime_divns(pvt->timeout, rate); in pvt_init_iface()
1050 pvt->timeout = ktime_add_ns(pvt->timeout, PVT_TOUT_MIN); in pvt_init_iface()
1054 if (!of_property_read_u32(pvt->dev->of_node, in pvt_init_iface()
1055 "baikal,pvt-temp-offset-millicelsius", &temp)) in pvt_init_iface()
1058 pvt_set_trim(pvt, trim); in pvt_init_iface()
1063 static int pvt_request_irq(struct pvt_hwmon *pvt) in pvt_request_irq() argument
1065 struct platform_device *pdev = to_platform_device(pvt->dev); in pvt_request_irq()
1068 pvt->irq = platform_get_irq(pdev, 0); in pvt_request_irq()
1069 if (pvt->irq < 0) in pvt_request_irq()
1070 return pvt->irq; in pvt_request_irq()
1072 ret = devm_request_threaded_irq(pvt->dev, pvt->irq, in pvt_request_irq()
1080 "pvt", pvt); in pvt_request_irq()
1082 dev_err(pvt->dev, "Couldn't request PVT IRQ\n"); in pvt_request_irq()
1089 static int pvt_create_hwmon(struct pvt_hwmon *pvt) in pvt_create_hwmon() argument
1091 pvt->hwmon = devm_hwmon_device_register_with_info(pvt->dev, "pvt", pvt, in pvt_create_hwmon()
1093 if (IS_ERR(pvt->hwmon)) { in pvt_create_hwmon()
1094 dev_err(pvt->dev, "Couldn't create hwmon device\n"); in pvt_create_hwmon()
1095 return PTR_ERR(pvt->hwmon); in pvt_create_hwmon()
1105 struct pvt_hwmon *pvt = data; in pvt_disable_iface() local
1107 mutex_lock(&pvt->iface_mtx); in pvt_disable_iface()
1108 pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0); in pvt_disable_iface()
1109 pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, in pvt_disable_iface()
1111 mutex_unlock(&pvt->iface_mtx); in pvt_disable_iface()
1114 static int pvt_enable_iface(struct pvt_hwmon *pvt) in pvt_enable_iface() argument
1118 ret = devm_add_action(pvt->dev, pvt_disable_iface, pvt); in pvt_enable_iface()
1120 dev_err(pvt->dev, "Can't add PVT disable interface action\n"); in pvt_enable_iface()
1127 * corresponding sysfs files are accessible from user-space, in pvt_enable_iface()
1130 mutex_lock(&pvt->iface_mtx); in pvt_enable_iface()
1131 pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0); in pvt_enable_iface()
1132 pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN); in pvt_enable_iface()
1133 mutex_unlock(&pvt->iface_mtx); in pvt_enable_iface()
1140 static int pvt_enable_iface(struct pvt_hwmon *pvt) in pvt_enable_iface() argument
1149 struct pvt_hwmon *pvt; in pvt_probe() local
1152 pvt = pvt_create_data(pdev); in pvt_probe()
1153 if (IS_ERR(pvt)) in pvt_probe()
1154 return PTR_ERR(pvt); in pvt_probe()
1156 ret = pvt_request_regs(pvt); in pvt_probe()
1160 ret = pvt_request_clks(pvt); in pvt_probe()
1164 ret = pvt_check_pwr(pvt); in pvt_probe()
1168 ret = pvt_init_iface(pvt); in pvt_probe()
1172 ret = pvt_request_irq(pvt); in pvt_probe()
1176 ret = pvt_create_hwmon(pvt); in pvt_probe()
1180 ret = pvt_enable_iface(pvt); in pvt_probe()
1188 { .compatible = "baikal,bt1-pvt" },
1196 .name = "bt1-pvt",
1203 MODULE_DESCRIPTION("Baikal-T1 PVT driver");