cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ts-nbus.c (9120B)


      1/*
      2 * NBUS driver for TS-4600 based boards
      3 *
      4 * Copyright (c) 2016 - Savoir-faire Linux
      5 * Author: Sebastien Bourdelin <sebastien.bourdelin@savoirfairelinux.com>
      6 *
      7 * This file is licensed under the terms of the GNU General Public
      8 * License version 2. This program is licensed "as is" without any
      9 * warranty of any kind, whether express or implied.
     10 *
     11 * This driver implements a GPIOs bit-banged bus, called the NBUS by Technologic
     12 * Systems. It is used to communicate with the peripherals in the FPGA on the
     13 * TS-4600 SoM.
     14 */
     15
     16#include <linux/bitops.h>
     17#include <linux/gpio/consumer.h>
     18#include <linux/kernel.h>
     19#include <linux/module.h>
     20#include <linux/mutex.h>
     21#include <linux/of_platform.h>
     22#include <linux/platform_device.h>
     23#include <linux/pwm.h>
     24#include <linux/ts-nbus.h>
     25
     26#define TS_NBUS_DIRECTION_IN  0
     27#define TS_NBUS_DIRECTION_OUT 1
     28#define TS_NBUS_WRITE_ADR 0
     29#define TS_NBUS_WRITE_VAL 1
     30
     31struct ts_nbus {
     32	struct pwm_device *pwm;
     33	struct gpio_descs *data;
     34	struct gpio_desc *csn;
     35	struct gpio_desc *txrx;
     36	struct gpio_desc *strobe;
     37	struct gpio_desc *ale;
     38	struct gpio_desc *rdy;
     39	struct mutex lock;
     40};
     41
     42/*
     43 * request all gpios required by the bus.
     44 */
     45static int ts_nbus_init_pdata(struct platform_device *pdev, struct ts_nbus
     46		*ts_nbus)
     47{
     48	ts_nbus->data = devm_gpiod_get_array(&pdev->dev, "ts,data",
     49			GPIOD_OUT_HIGH);
     50	if (IS_ERR(ts_nbus->data)) {
     51		dev_err(&pdev->dev, "failed to retrieve ts,data-gpio from dts\n");
     52		return PTR_ERR(ts_nbus->data);
     53	}
     54
     55	ts_nbus->csn = devm_gpiod_get(&pdev->dev, "ts,csn", GPIOD_OUT_HIGH);
     56	if (IS_ERR(ts_nbus->csn)) {
     57		dev_err(&pdev->dev, "failed to retrieve ts,csn-gpio from dts\n");
     58		return PTR_ERR(ts_nbus->csn);
     59	}
     60
     61	ts_nbus->txrx = devm_gpiod_get(&pdev->dev, "ts,txrx", GPIOD_OUT_HIGH);
     62	if (IS_ERR(ts_nbus->txrx)) {
     63		dev_err(&pdev->dev, "failed to retrieve ts,txrx-gpio from dts\n");
     64		return PTR_ERR(ts_nbus->txrx);
     65	}
     66
     67	ts_nbus->strobe = devm_gpiod_get(&pdev->dev, "ts,strobe", GPIOD_OUT_HIGH);
     68	if (IS_ERR(ts_nbus->strobe)) {
     69		dev_err(&pdev->dev, "failed to retrieve ts,strobe-gpio from dts\n");
     70		return PTR_ERR(ts_nbus->strobe);
     71	}
     72
     73	ts_nbus->ale = devm_gpiod_get(&pdev->dev, "ts,ale", GPIOD_OUT_HIGH);
     74	if (IS_ERR(ts_nbus->ale)) {
     75		dev_err(&pdev->dev, "failed to retrieve ts,ale-gpio from dts\n");
     76		return PTR_ERR(ts_nbus->ale);
     77	}
     78
     79	ts_nbus->rdy = devm_gpiod_get(&pdev->dev, "ts,rdy", GPIOD_IN);
     80	if (IS_ERR(ts_nbus->rdy)) {
     81		dev_err(&pdev->dev, "failed to retrieve ts,rdy-gpio from dts\n");
     82		return PTR_ERR(ts_nbus->rdy);
     83	}
     84
     85	return 0;
     86}
     87
     88/*
     89 * the data gpios are used for reading and writing values, their directions
     90 * should be adjusted accordingly.
     91 */
     92static void ts_nbus_set_direction(struct ts_nbus *ts_nbus, int direction)
     93{
     94	int i;
     95
     96	for (i = 0; i < 8; i++) {
     97		if (direction == TS_NBUS_DIRECTION_IN)
     98			gpiod_direction_input(ts_nbus->data->desc[i]);
     99		else
    100			/* when used as output the default state of the data
    101			 * lines are set to high */
    102			gpiod_direction_output(ts_nbus->data->desc[i], 1);
    103	}
    104}
    105
    106/*
    107 * reset the bus in its initial state.
    108 * The data, csn, strobe and ale lines must be zero'ed to let the FPGA knows a
    109 * new transaction can be process.
    110 */
    111static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus)
    112{
    113	DECLARE_BITMAP(values, 8);
    114
    115	values[0] = 0;
    116
    117	gpiod_set_array_value_cansleep(8, ts_nbus->data->desc,
    118				       ts_nbus->data->info, values);
    119	gpiod_set_value_cansleep(ts_nbus->csn, 0);
    120	gpiod_set_value_cansleep(ts_nbus->strobe, 0);
    121	gpiod_set_value_cansleep(ts_nbus->ale, 0);
    122}
    123
    124/*
    125 * let the FPGA knows it can process.
    126 */
    127static void ts_nbus_start_transaction(struct ts_nbus *ts_nbus)
    128{
    129	gpiod_set_value_cansleep(ts_nbus->strobe, 1);
    130}
    131
    132/*
    133 * read a byte value from the data gpios.
    134 * return 0 on success or negative errno on failure.
    135 */
    136static int ts_nbus_read_byte(struct ts_nbus *ts_nbus, u8 *val)
    137{
    138	struct gpio_descs *gpios = ts_nbus->data;
    139	int ret, i;
    140
    141	*val = 0;
    142	for (i = 0; i < 8; i++) {
    143		ret = gpiod_get_value_cansleep(gpios->desc[i]);
    144		if (ret < 0)
    145			return ret;
    146		if (ret)
    147			*val |= BIT(i);
    148	}
    149
    150	return 0;
    151}
    152
    153/*
    154 * set the data gpios accordingly to the byte value.
    155 */
    156static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte)
    157{
    158	struct gpio_descs *gpios = ts_nbus->data;
    159	DECLARE_BITMAP(values, 8);
    160
    161	values[0] = byte;
    162
    163	gpiod_set_array_value_cansleep(8, gpios->desc, gpios->info, values);
    164}
    165
    166/*
    167 * reading the bus consists of resetting the bus, then notifying the FPGA to
    168 * send the data in the data gpios and return the read value.
    169 * return 0 on success or negative errno on failure.
    170 */
    171static int ts_nbus_read_bus(struct ts_nbus *ts_nbus, u8 *val)
    172{
    173	ts_nbus_reset_bus(ts_nbus);
    174	ts_nbus_start_transaction(ts_nbus);
    175
    176	return ts_nbus_read_byte(ts_nbus, val);
    177}
    178
    179/*
    180 * writing to the bus consists of resetting the bus, then define the type of
    181 * command (address/value), write the data and notify the FPGA to retrieve the
    182 * value in the data gpios.
    183 */
    184static void ts_nbus_write_bus(struct ts_nbus *ts_nbus, int cmd, u8 val)
    185{
    186	ts_nbus_reset_bus(ts_nbus);
    187
    188	if (cmd == TS_NBUS_WRITE_ADR)
    189		gpiod_set_value_cansleep(ts_nbus->ale, 1);
    190
    191	ts_nbus_write_byte(ts_nbus, val);
    192	ts_nbus_start_transaction(ts_nbus);
    193}
    194
    195/*
    196 * read the value in the FPGA register at the given address.
    197 * return 0 on success or negative errno on failure.
    198 */
    199int ts_nbus_read(struct ts_nbus *ts_nbus, u8 adr, u16 *val)
    200{
    201	int ret, i;
    202	u8 byte;
    203
    204	/* bus access must be atomic */
    205	mutex_lock(&ts_nbus->lock);
    206
    207	/* set the bus in read mode */
    208	gpiod_set_value_cansleep(ts_nbus->txrx, 0);
    209
    210	/* write address */
    211	ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_ADR, adr);
    212
    213	/* set the data gpios direction as input before reading */
    214	ts_nbus_set_direction(ts_nbus, TS_NBUS_DIRECTION_IN);
    215
    216	/* reading value MSB first */
    217	do {
    218		*val = 0;
    219		byte = 0;
    220		for (i = 1; i >= 0; i--) {
    221			/* read a byte from the bus, leave on error */
    222			ret = ts_nbus_read_bus(ts_nbus, &byte);
    223			if (ret < 0)
    224				goto err;
    225
    226			/* append the byte read to the final value */
    227			*val |= byte << (i * 8);
    228		}
    229		gpiod_set_value_cansleep(ts_nbus->csn, 1);
    230		ret = gpiod_get_value_cansleep(ts_nbus->rdy);
    231	} while (ret);
    232
    233err:
    234	/* restore the data gpios direction as output after reading */
    235	ts_nbus_set_direction(ts_nbus, TS_NBUS_DIRECTION_OUT);
    236
    237	mutex_unlock(&ts_nbus->lock);
    238
    239	return ret;
    240}
    241EXPORT_SYMBOL_GPL(ts_nbus_read);
    242
    243/*
    244 * write the desired value in the FPGA register at the given address.
    245 */
    246int ts_nbus_write(struct ts_nbus *ts_nbus, u8 adr, u16 val)
    247{
    248	int i;
    249
    250	/* bus access must be atomic */
    251	mutex_lock(&ts_nbus->lock);
    252
    253	/* set the bus in write mode */
    254	gpiod_set_value_cansleep(ts_nbus->txrx, 1);
    255
    256	/* write address */
    257	ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_ADR, adr);
    258
    259	/* writing value MSB first */
    260	for (i = 1; i >= 0; i--)
    261		ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_VAL, (u8)(val >> (i * 8)));
    262
    263	/* wait for completion */
    264	gpiod_set_value_cansleep(ts_nbus->csn, 1);
    265	while (gpiod_get_value_cansleep(ts_nbus->rdy) != 0) {
    266		gpiod_set_value_cansleep(ts_nbus->csn, 0);
    267		gpiod_set_value_cansleep(ts_nbus->csn, 1);
    268	}
    269
    270	mutex_unlock(&ts_nbus->lock);
    271
    272	return 0;
    273}
    274EXPORT_SYMBOL_GPL(ts_nbus_write);
    275
    276static int ts_nbus_probe(struct platform_device *pdev)
    277{
    278	struct pwm_device *pwm;
    279	struct pwm_args pargs;
    280	struct device *dev = &pdev->dev;
    281	struct ts_nbus *ts_nbus;
    282	int ret;
    283
    284	ts_nbus = devm_kzalloc(dev, sizeof(*ts_nbus), GFP_KERNEL);
    285	if (!ts_nbus)
    286		return -ENOMEM;
    287
    288	mutex_init(&ts_nbus->lock);
    289
    290	ret = ts_nbus_init_pdata(pdev, ts_nbus);
    291	if (ret < 0)
    292		return ret;
    293
    294	pwm = devm_pwm_get(dev, NULL);
    295	if (IS_ERR(pwm)) {
    296		ret = PTR_ERR(pwm);
    297		if (ret != -EPROBE_DEFER)
    298			dev_err(dev, "unable to request PWM\n");
    299		return ret;
    300	}
    301
    302	pwm_get_args(pwm, &pargs);
    303	if (!pargs.period) {
    304		dev_err(&pdev->dev, "invalid PWM period\n");
    305		return -EINVAL;
    306	}
    307
    308	/*
    309	 * FIXME: pwm_apply_args() should be removed when switching to
    310	 * the atomic PWM API.
    311	 */
    312	pwm_apply_args(pwm);
    313	ret = pwm_config(pwm, pargs.period, pargs.period);
    314	if (ret < 0)
    315		return ret;
    316
    317	/*
    318	 * we can now start the FPGA and populate the peripherals.
    319	 */
    320	pwm_enable(pwm);
    321	ts_nbus->pwm = pwm;
    322
    323	/*
    324	 * let the child nodes retrieve this instance of the ts-nbus.
    325	 */
    326	dev_set_drvdata(dev, ts_nbus);
    327
    328	ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
    329	if (ret < 0)
    330		return ret;
    331
    332	dev_info(dev, "initialized\n");
    333
    334	return 0;
    335}
    336
    337static int ts_nbus_remove(struct platform_device *pdev)
    338{
    339	struct ts_nbus *ts_nbus = dev_get_drvdata(&pdev->dev);
    340
    341	/* shutdown the FPGA */
    342	mutex_lock(&ts_nbus->lock);
    343	pwm_disable(ts_nbus->pwm);
    344	mutex_unlock(&ts_nbus->lock);
    345
    346	return 0;
    347}
    348
    349static const struct of_device_id ts_nbus_of_match[] = {
    350	{ .compatible = "technologic,ts-nbus", },
    351	{ },
    352};
    353MODULE_DEVICE_TABLE(of, ts_nbus_of_match);
    354
    355static struct platform_driver ts_nbus_driver = {
    356	.probe		= ts_nbus_probe,
    357	.remove		= ts_nbus_remove,
    358	.driver		= {
    359		.name	= "ts_nbus",
    360		.of_match_table = ts_nbus_of_match,
    361	},
    362};
    363
    364module_platform_driver(ts_nbus_driver);
    365
    366MODULE_ALIAS("platform:ts_nbus");
    367MODULE_AUTHOR("Sebastien Bourdelin <sebastien.bourdelin@savoirfairelinux.com>");
    368MODULE_DESCRIPTION("Technologic Systems NBUS");
    369MODULE_LICENSE("GPL v2");