xref: /openbmc/linux/drivers/bus/ts-nbus.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1*3bb16560SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25b143d2aSSebastien Bourdelin /*
35b143d2aSSebastien Bourdelin  * NBUS driver for TS-4600 based boards
45b143d2aSSebastien Bourdelin  *
55b143d2aSSebastien Bourdelin  * Copyright (c) 2016 - Savoir-faire Linux
65b143d2aSSebastien Bourdelin  * Author: Sebastien Bourdelin <sebastien.bourdelin@savoirfairelinux.com>
75b143d2aSSebastien Bourdelin  *
85b143d2aSSebastien Bourdelin  * This driver implements a GPIOs bit-banged bus, called the NBUS by Technologic
95b143d2aSSebastien Bourdelin  * Systems. It is used to communicate with the peripherals in the FPGA on the
105b143d2aSSebastien Bourdelin  * TS-4600 SoM.
115b143d2aSSebastien Bourdelin  */
125b143d2aSSebastien Bourdelin 
135b143d2aSSebastien Bourdelin #include <linux/bitops.h>
145b143d2aSSebastien Bourdelin #include <linux/gpio/consumer.h>
155b143d2aSSebastien Bourdelin #include <linux/kernel.h>
165b143d2aSSebastien Bourdelin #include <linux/module.h>
175b143d2aSSebastien Bourdelin #include <linux/mutex.h>
185b143d2aSSebastien Bourdelin #include <linux/of_platform.h>
195b143d2aSSebastien Bourdelin #include <linux/platform_device.h>
205b143d2aSSebastien Bourdelin #include <linux/pwm.h>
215b143d2aSSebastien Bourdelin #include <linux/ts-nbus.h>
225b143d2aSSebastien Bourdelin 
235b143d2aSSebastien Bourdelin #define TS_NBUS_DIRECTION_IN  0
245b143d2aSSebastien Bourdelin #define TS_NBUS_DIRECTION_OUT 1
255b143d2aSSebastien Bourdelin #define TS_NBUS_WRITE_ADR 0
265b143d2aSSebastien Bourdelin #define TS_NBUS_WRITE_VAL 1
275b143d2aSSebastien Bourdelin 
285b143d2aSSebastien Bourdelin struct ts_nbus {
295b143d2aSSebastien Bourdelin 	struct pwm_device *pwm;
305b143d2aSSebastien Bourdelin 	struct gpio_descs *data;
315b143d2aSSebastien Bourdelin 	struct gpio_desc *csn;
325b143d2aSSebastien Bourdelin 	struct gpio_desc *txrx;
335b143d2aSSebastien Bourdelin 	struct gpio_desc *strobe;
345b143d2aSSebastien Bourdelin 	struct gpio_desc *ale;
355b143d2aSSebastien Bourdelin 	struct gpio_desc *rdy;
365b143d2aSSebastien Bourdelin 	struct mutex lock;
375b143d2aSSebastien Bourdelin };
385b143d2aSSebastien Bourdelin 
395b143d2aSSebastien Bourdelin /*
405b143d2aSSebastien Bourdelin  * request all gpios required by the bus.
415b143d2aSSebastien Bourdelin  */
ts_nbus_init_pdata(struct platform_device * pdev,struct ts_nbus * ts_nbus)425b143d2aSSebastien Bourdelin static int ts_nbus_init_pdata(struct platform_device *pdev, struct ts_nbus
435b143d2aSSebastien Bourdelin 		*ts_nbus)
445b143d2aSSebastien Bourdelin {
455b143d2aSSebastien Bourdelin 	ts_nbus->data = devm_gpiod_get_array(&pdev->dev, "ts,data",
465b143d2aSSebastien Bourdelin 			GPIOD_OUT_HIGH);
475b143d2aSSebastien Bourdelin 	if (IS_ERR(ts_nbus->data)) {
485b143d2aSSebastien Bourdelin 		dev_err(&pdev->dev, "failed to retrieve ts,data-gpio from dts\n");
495b143d2aSSebastien Bourdelin 		return PTR_ERR(ts_nbus->data);
505b143d2aSSebastien Bourdelin 	}
515b143d2aSSebastien Bourdelin 
525b143d2aSSebastien Bourdelin 	ts_nbus->csn = devm_gpiod_get(&pdev->dev, "ts,csn", GPIOD_OUT_HIGH);
535b143d2aSSebastien Bourdelin 	if (IS_ERR(ts_nbus->csn)) {
545b143d2aSSebastien Bourdelin 		dev_err(&pdev->dev, "failed to retrieve ts,csn-gpio from dts\n");
555b143d2aSSebastien Bourdelin 		return PTR_ERR(ts_nbus->csn);
565b143d2aSSebastien Bourdelin 	}
575b143d2aSSebastien Bourdelin 
585b143d2aSSebastien Bourdelin 	ts_nbus->txrx = devm_gpiod_get(&pdev->dev, "ts,txrx", GPIOD_OUT_HIGH);
595b143d2aSSebastien Bourdelin 	if (IS_ERR(ts_nbus->txrx)) {
605b143d2aSSebastien Bourdelin 		dev_err(&pdev->dev, "failed to retrieve ts,txrx-gpio from dts\n");
615b143d2aSSebastien Bourdelin 		return PTR_ERR(ts_nbus->txrx);
625b143d2aSSebastien Bourdelin 	}
635b143d2aSSebastien Bourdelin 
645b143d2aSSebastien Bourdelin 	ts_nbus->strobe = devm_gpiod_get(&pdev->dev, "ts,strobe", GPIOD_OUT_HIGH);
655b143d2aSSebastien Bourdelin 	if (IS_ERR(ts_nbus->strobe)) {
665b143d2aSSebastien Bourdelin 		dev_err(&pdev->dev, "failed to retrieve ts,strobe-gpio from dts\n");
675b143d2aSSebastien Bourdelin 		return PTR_ERR(ts_nbus->strobe);
685b143d2aSSebastien Bourdelin 	}
695b143d2aSSebastien Bourdelin 
705b143d2aSSebastien Bourdelin 	ts_nbus->ale = devm_gpiod_get(&pdev->dev, "ts,ale", GPIOD_OUT_HIGH);
715b143d2aSSebastien Bourdelin 	if (IS_ERR(ts_nbus->ale)) {
725b143d2aSSebastien Bourdelin 		dev_err(&pdev->dev, "failed to retrieve ts,ale-gpio from dts\n");
735b143d2aSSebastien Bourdelin 		return PTR_ERR(ts_nbus->ale);
745b143d2aSSebastien Bourdelin 	}
755b143d2aSSebastien Bourdelin 
765b143d2aSSebastien Bourdelin 	ts_nbus->rdy = devm_gpiod_get(&pdev->dev, "ts,rdy", GPIOD_IN);
775b143d2aSSebastien Bourdelin 	if (IS_ERR(ts_nbus->rdy)) {
785b143d2aSSebastien Bourdelin 		dev_err(&pdev->dev, "failed to retrieve ts,rdy-gpio from dts\n");
795b143d2aSSebastien Bourdelin 		return PTR_ERR(ts_nbus->rdy);
805b143d2aSSebastien Bourdelin 	}
815b143d2aSSebastien Bourdelin 
825b143d2aSSebastien Bourdelin 	return 0;
835b143d2aSSebastien Bourdelin }
845b143d2aSSebastien Bourdelin 
855b143d2aSSebastien Bourdelin /*
865b143d2aSSebastien Bourdelin  * the data gpios are used for reading and writing values, their directions
875b143d2aSSebastien Bourdelin  * should be adjusted accordingly.
885b143d2aSSebastien Bourdelin  */
ts_nbus_set_direction(struct ts_nbus * ts_nbus,int direction)895b143d2aSSebastien Bourdelin static void ts_nbus_set_direction(struct ts_nbus *ts_nbus, int direction)
905b143d2aSSebastien Bourdelin {
915b143d2aSSebastien Bourdelin 	int i;
925b143d2aSSebastien Bourdelin 
935b143d2aSSebastien Bourdelin 	for (i = 0; i < 8; i++) {
945b143d2aSSebastien Bourdelin 		if (direction == TS_NBUS_DIRECTION_IN)
955b143d2aSSebastien Bourdelin 			gpiod_direction_input(ts_nbus->data->desc[i]);
965b143d2aSSebastien Bourdelin 		else
975b143d2aSSebastien Bourdelin 			/* when used as output the default state of the data
985b143d2aSSebastien Bourdelin 			 * lines are set to high */
995b143d2aSSebastien Bourdelin 			gpiod_direction_output(ts_nbus->data->desc[i], 1);
1005b143d2aSSebastien Bourdelin 	}
1015b143d2aSSebastien Bourdelin }
1025b143d2aSSebastien Bourdelin 
1035b143d2aSSebastien Bourdelin /*
1045b143d2aSSebastien Bourdelin  * reset the bus in its initial state.
1055b143d2aSSebastien Bourdelin  * The data, csn, strobe and ale lines must be zero'ed to let the FPGA knows a
1065b143d2aSSebastien Bourdelin  * new transaction can be process.
1075b143d2aSSebastien Bourdelin  */
ts_nbus_reset_bus(struct ts_nbus * ts_nbus)1085b143d2aSSebastien Bourdelin static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus)
1095b143d2aSSebastien Bourdelin {
110b9762bebSJanusz Krzysztofik 	DECLARE_BITMAP(values, 8);
1115b143d2aSSebastien Bourdelin 
112b9762bebSJanusz Krzysztofik 	values[0] = 0;
1135b143d2aSSebastien Bourdelin 
11477588c14SJanusz Krzysztofik 	gpiod_set_array_value_cansleep(8, ts_nbus->data->desc,
11577588c14SJanusz Krzysztofik 				       ts_nbus->data->info, values);
1165b143d2aSSebastien Bourdelin 	gpiod_set_value_cansleep(ts_nbus->csn, 0);
1175b143d2aSSebastien Bourdelin 	gpiod_set_value_cansleep(ts_nbus->strobe, 0);
1185b143d2aSSebastien Bourdelin 	gpiod_set_value_cansleep(ts_nbus->ale, 0);
1195b143d2aSSebastien Bourdelin }
1205b143d2aSSebastien Bourdelin 
1215b143d2aSSebastien Bourdelin /*
1225b143d2aSSebastien Bourdelin  * let the FPGA knows it can process.
1235b143d2aSSebastien Bourdelin  */
ts_nbus_start_transaction(struct ts_nbus * ts_nbus)1245b143d2aSSebastien Bourdelin static void ts_nbus_start_transaction(struct ts_nbus *ts_nbus)
1255b143d2aSSebastien Bourdelin {
1265b143d2aSSebastien Bourdelin 	gpiod_set_value_cansleep(ts_nbus->strobe, 1);
1275b143d2aSSebastien Bourdelin }
1285b143d2aSSebastien Bourdelin 
1295b143d2aSSebastien Bourdelin /*
1305b143d2aSSebastien Bourdelin  * read a byte value from the data gpios.
1315b143d2aSSebastien Bourdelin  * return 0 on success or negative errno on failure.
1325b143d2aSSebastien Bourdelin  */
ts_nbus_read_byte(struct ts_nbus * ts_nbus,u8 * val)1335b143d2aSSebastien Bourdelin static int ts_nbus_read_byte(struct ts_nbus *ts_nbus, u8 *val)
1345b143d2aSSebastien Bourdelin {
1355b143d2aSSebastien Bourdelin 	struct gpio_descs *gpios = ts_nbus->data;
1365b143d2aSSebastien Bourdelin 	int ret, i;
1375b143d2aSSebastien Bourdelin 
1385b143d2aSSebastien Bourdelin 	*val = 0;
1395b143d2aSSebastien Bourdelin 	for (i = 0; i < 8; i++) {
1405b143d2aSSebastien Bourdelin 		ret = gpiod_get_value_cansleep(gpios->desc[i]);
1415b143d2aSSebastien Bourdelin 		if (ret < 0)
1425b143d2aSSebastien Bourdelin 			return ret;
1435b143d2aSSebastien Bourdelin 		if (ret)
1445b143d2aSSebastien Bourdelin 			*val |= BIT(i);
1455b143d2aSSebastien Bourdelin 	}
1465b143d2aSSebastien Bourdelin 
1475b143d2aSSebastien Bourdelin 	return 0;
1485b143d2aSSebastien Bourdelin }
1495b143d2aSSebastien Bourdelin 
1505b143d2aSSebastien Bourdelin /*
1515b143d2aSSebastien Bourdelin  * set the data gpios accordingly to the byte value.
1525b143d2aSSebastien Bourdelin  */
ts_nbus_write_byte(struct ts_nbus * ts_nbus,u8 byte)1535b143d2aSSebastien Bourdelin static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte)
1545b143d2aSSebastien Bourdelin {
1555b143d2aSSebastien Bourdelin 	struct gpio_descs *gpios = ts_nbus->data;
156b9762bebSJanusz Krzysztofik 	DECLARE_BITMAP(values, 8);
1575b143d2aSSebastien Bourdelin 
158b9762bebSJanusz Krzysztofik 	values[0] = byte;
1595b143d2aSSebastien Bourdelin 
16077588c14SJanusz Krzysztofik 	gpiod_set_array_value_cansleep(8, gpios->desc, gpios->info, values);
1615b143d2aSSebastien Bourdelin }
1625b143d2aSSebastien Bourdelin 
1635b143d2aSSebastien Bourdelin /*
1645b143d2aSSebastien Bourdelin  * reading the bus consists of resetting the bus, then notifying the FPGA to
1655b143d2aSSebastien Bourdelin  * send the data in the data gpios and return the read value.
1665b143d2aSSebastien Bourdelin  * return 0 on success or negative errno on failure.
1675b143d2aSSebastien Bourdelin  */
ts_nbus_read_bus(struct ts_nbus * ts_nbus,u8 * val)1685b143d2aSSebastien Bourdelin static int ts_nbus_read_bus(struct ts_nbus *ts_nbus, u8 *val)
1695b143d2aSSebastien Bourdelin {
1705b143d2aSSebastien Bourdelin 	ts_nbus_reset_bus(ts_nbus);
1715b143d2aSSebastien Bourdelin 	ts_nbus_start_transaction(ts_nbus);
1725b143d2aSSebastien Bourdelin 
1735b143d2aSSebastien Bourdelin 	return ts_nbus_read_byte(ts_nbus, val);
1745b143d2aSSebastien Bourdelin }
1755b143d2aSSebastien Bourdelin 
1765b143d2aSSebastien Bourdelin /*
1775b143d2aSSebastien Bourdelin  * writing to the bus consists of resetting the bus, then define the type of
1785b143d2aSSebastien Bourdelin  * command (address/value), write the data and notify the FPGA to retrieve the
1795b143d2aSSebastien Bourdelin  * value in the data gpios.
1805b143d2aSSebastien Bourdelin  */
ts_nbus_write_bus(struct ts_nbus * ts_nbus,int cmd,u8 val)1815b143d2aSSebastien Bourdelin static void ts_nbus_write_bus(struct ts_nbus *ts_nbus, int cmd, u8 val)
1825b143d2aSSebastien Bourdelin {
1835b143d2aSSebastien Bourdelin 	ts_nbus_reset_bus(ts_nbus);
1845b143d2aSSebastien Bourdelin 
1855b143d2aSSebastien Bourdelin 	if (cmd == TS_NBUS_WRITE_ADR)
1865b143d2aSSebastien Bourdelin 		gpiod_set_value_cansleep(ts_nbus->ale, 1);
1875b143d2aSSebastien Bourdelin 
1885b143d2aSSebastien Bourdelin 	ts_nbus_write_byte(ts_nbus, val);
1895b143d2aSSebastien Bourdelin 	ts_nbus_start_transaction(ts_nbus);
1905b143d2aSSebastien Bourdelin }
1915b143d2aSSebastien Bourdelin 
1925b143d2aSSebastien Bourdelin /*
1935b143d2aSSebastien Bourdelin  * read the value in the FPGA register at the given address.
1945b143d2aSSebastien Bourdelin  * return 0 on success or negative errno on failure.
1955b143d2aSSebastien Bourdelin  */
ts_nbus_read(struct ts_nbus * ts_nbus,u8 adr,u16 * val)1965b143d2aSSebastien Bourdelin int ts_nbus_read(struct ts_nbus *ts_nbus, u8 adr, u16 *val)
1975b143d2aSSebastien Bourdelin {
1985b143d2aSSebastien Bourdelin 	int ret, i;
1995b143d2aSSebastien Bourdelin 	u8 byte;
2005b143d2aSSebastien Bourdelin 
2015b143d2aSSebastien Bourdelin 	/* bus access must be atomic */
2025b143d2aSSebastien Bourdelin 	mutex_lock(&ts_nbus->lock);
2035b143d2aSSebastien Bourdelin 
2045b143d2aSSebastien Bourdelin 	/* set the bus in read mode */
2055b143d2aSSebastien Bourdelin 	gpiod_set_value_cansleep(ts_nbus->txrx, 0);
2065b143d2aSSebastien Bourdelin 
2075b143d2aSSebastien Bourdelin 	/* write address */
2085b143d2aSSebastien Bourdelin 	ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_ADR, adr);
2095b143d2aSSebastien Bourdelin 
2105b143d2aSSebastien Bourdelin 	/* set the data gpios direction as input before reading */
2115b143d2aSSebastien Bourdelin 	ts_nbus_set_direction(ts_nbus, TS_NBUS_DIRECTION_IN);
2125b143d2aSSebastien Bourdelin 
2135b143d2aSSebastien Bourdelin 	/* reading value MSB first */
2145b143d2aSSebastien Bourdelin 	do {
2155b143d2aSSebastien Bourdelin 		*val = 0;
2165b143d2aSSebastien Bourdelin 		byte = 0;
2175b143d2aSSebastien Bourdelin 		for (i = 1; i >= 0; i--) {
2185b143d2aSSebastien Bourdelin 			/* read a byte from the bus, leave on error */
2195b143d2aSSebastien Bourdelin 			ret = ts_nbus_read_bus(ts_nbus, &byte);
2205b143d2aSSebastien Bourdelin 			if (ret < 0)
2215b143d2aSSebastien Bourdelin 				goto err;
2225b143d2aSSebastien Bourdelin 
2235b143d2aSSebastien Bourdelin 			/* append the byte read to the final value */
2245b143d2aSSebastien Bourdelin 			*val |= byte << (i * 8);
2255b143d2aSSebastien Bourdelin 		}
2265b143d2aSSebastien Bourdelin 		gpiod_set_value_cansleep(ts_nbus->csn, 1);
2275b143d2aSSebastien Bourdelin 		ret = gpiod_get_value_cansleep(ts_nbus->rdy);
2285b143d2aSSebastien Bourdelin 	} while (ret);
2295b143d2aSSebastien Bourdelin 
2305b143d2aSSebastien Bourdelin err:
2315b143d2aSSebastien Bourdelin 	/* restore the data gpios direction as output after reading */
2325b143d2aSSebastien Bourdelin 	ts_nbus_set_direction(ts_nbus, TS_NBUS_DIRECTION_OUT);
2335b143d2aSSebastien Bourdelin 
2345b143d2aSSebastien Bourdelin 	mutex_unlock(&ts_nbus->lock);
2355b143d2aSSebastien Bourdelin 
2365b143d2aSSebastien Bourdelin 	return ret;
2375b143d2aSSebastien Bourdelin }
2385b143d2aSSebastien Bourdelin EXPORT_SYMBOL_GPL(ts_nbus_read);
2395b143d2aSSebastien Bourdelin 
2405b143d2aSSebastien Bourdelin /*
2415b143d2aSSebastien Bourdelin  * write the desired value in the FPGA register at the given address.
2425b143d2aSSebastien Bourdelin  */
ts_nbus_write(struct ts_nbus * ts_nbus,u8 adr,u16 val)2435b143d2aSSebastien Bourdelin int ts_nbus_write(struct ts_nbus *ts_nbus, u8 adr, u16 val)
2445b143d2aSSebastien Bourdelin {
2455b143d2aSSebastien Bourdelin 	int i;
2465b143d2aSSebastien Bourdelin 
2475b143d2aSSebastien Bourdelin 	/* bus access must be atomic */
2485b143d2aSSebastien Bourdelin 	mutex_lock(&ts_nbus->lock);
2495b143d2aSSebastien Bourdelin 
2505b143d2aSSebastien Bourdelin 	/* set the bus in write mode */
2515b143d2aSSebastien Bourdelin 	gpiod_set_value_cansleep(ts_nbus->txrx, 1);
2525b143d2aSSebastien Bourdelin 
2535b143d2aSSebastien Bourdelin 	/* write address */
2545b143d2aSSebastien Bourdelin 	ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_ADR, adr);
2555b143d2aSSebastien Bourdelin 
2565b143d2aSSebastien Bourdelin 	/* writing value MSB first */
2575b143d2aSSebastien Bourdelin 	for (i = 1; i >= 0; i--)
2585b143d2aSSebastien Bourdelin 		ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_VAL, (u8)(val >> (i * 8)));
2595b143d2aSSebastien Bourdelin 
2605b143d2aSSebastien Bourdelin 	/* wait for completion */
2615b143d2aSSebastien Bourdelin 	gpiod_set_value_cansleep(ts_nbus->csn, 1);
2625b143d2aSSebastien Bourdelin 	while (gpiod_get_value_cansleep(ts_nbus->rdy) != 0) {
2635b143d2aSSebastien Bourdelin 		gpiod_set_value_cansleep(ts_nbus->csn, 0);
2645b143d2aSSebastien Bourdelin 		gpiod_set_value_cansleep(ts_nbus->csn, 1);
2655b143d2aSSebastien Bourdelin 	}
2665b143d2aSSebastien Bourdelin 
2675b143d2aSSebastien Bourdelin 	mutex_unlock(&ts_nbus->lock);
2685b143d2aSSebastien Bourdelin 
2695b143d2aSSebastien Bourdelin 	return 0;
2705b143d2aSSebastien Bourdelin }
2715b143d2aSSebastien Bourdelin EXPORT_SYMBOL_GPL(ts_nbus_write);
2725b143d2aSSebastien Bourdelin 
ts_nbus_probe(struct platform_device * pdev)2735b143d2aSSebastien Bourdelin static int ts_nbus_probe(struct platform_device *pdev)
2745b143d2aSSebastien Bourdelin {
2755b143d2aSSebastien Bourdelin 	struct pwm_device *pwm;
2765b143d2aSSebastien Bourdelin 	struct pwm_args pargs;
2775b143d2aSSebastien Bourdelin 	struct device *dev = &pdev->dev;
2785b143d2aSSebastien Bourdelin 	struct ts_nbus *ts_nbus;
2795b143d2aSSebastien Bourdelin 	int ret;
2805b143d2aSSebastien Bourdelin 
2815b143d2aSSebastien Bourdelin 	ts_nbus = devm_kzalloc(dev, sizeof(*ts_nbus), GFP_KERNEL);
2825b143d2aSSebastien Bourdelin 	if (!ts_nbus)
2835b143d2aSSebastien Bourdelin 		return -ENOMEM;
2845b143d2aSSebastien Bourdelin 
2855b143d2aSSebastien Bourdelin 	mutex_init(&ts_nbus->lock);
2865b143d2aSSebastien Bourdelin 
2875b143d2aSSebastien Bourdelin 	ret = ts_nbus_init_pdata(pdev, ts_nbus);
2885b143d2aSSebastien Bourdelin 	if (ret < 0)
2895b143d2aSSebastien Bourdelin 		return ret;
2905b143d2aSSebastien Bourdelin 
2915b143d2aSSebastien Bourdelin 	pwm = devm_pwm_get(dev, NULL);
2925b143d2aSSebastien Bourdelin 	if (IS_ERR(pwm)) {
2935b143d2aSSebastien Bourdelin 		ret = PTR_ERR(pwm);
2945b143d2aSSebastien Bourdelin 		if (ret != -EPROBE_DEFER)
2955b143d2aSSebastien Bourdelin 			dev_err(dev, "unable to request PWM\n");
2965b143d2aSSebastien Bourdelin 		return ret;
2975b143d2aSSebastien Bourdelin 	}
2985b143d2aSSebastien Bourdelin 
2995b143d2aSSebastien Bourdelin 	pwm_get_args(pwm, &pargs);
3005b143d2aSSebastien Bourdelin 	if (!pargs.period) {
3015b143d2aSSebastien Bourdelin 		dev_err(&pdev->dev, "invalid PWM period\n");
3025b143d2aSSebastien Bourdelin 		return -EINVAL;
3035b143d2aSSebastien Bourdelin 	}
3045b143d2aSSebastien Bourdelin 
3055b143d2aSSebastien Bourdelin 	/*
3065b143d2aSSebastien Bourdelin 	 * FIXME: pwm_apply_args() should be removed when switching to
3075b143d2aSSebastien Bourdelin 	 * the atomic PWM API.
3085b143d2aSSebastien Bourdelin 	 */
3095b143d2aSSebastien Bourdelin 	pwm_apply_args(pwm);
3105b143d2aSSebastien Bourdelin 	ret = pwm_config(pwm, pargs.period, pargs.period);
3115b143d2aSSebastien Bourdelin 	if (ret < 0)
3125b143d2aSSebastien Bourdelin 		return ret;
3135b143d2aSSebastien Bourdelin 
3145b143d2aSSebastien Bourdelin 	/*
3155b143d2aSSebastien Bourdelin 	 * we can now start the FPGA and populate the peripherals.
3165b143d2aSSebastien Bourdelin 	 */
3175b143d2aSSebastien Bourdelin 	pwm_enable(pwm);
3185b143d2aSSebastien Bourdelin 	ts_nbus->pwm = pwm;
3195b143d2aSSebastien Bourdelin 
3205b143d2aSSebastien Bourdelin 	/*
3215b143d2aSSebastien Bourdelin 	 * let the child nodes retrieve this instance of the ts-nbus.
3225b143d2aSSebastien Bourdelin 	 */
3235b143d2aSSebastien Bourdelin 	dev_set_drvdata(dev, ts_nbus);
3245b143d2aSSebastien Bourdelin 
3255b143d2aSSebastien Bourdelin 	ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
3265b143d2aSSebastien Bourdelin 	if (ret < 0)
3275b143d2aSSebastien Bourdelin 		return ret;
3285b143d2aSSebastien Bourdelin 
3295b143d2aSSebastien Bourdelin 	dev_info(dev, "initialized\n");
3305b143d2aSSebastien Bourdelin 
3315b143d2aSSebastien Bourdelin 	return 0;
3325b143d2aSSebastien Bourdelin }
3335b143d2aSSebastien Bourdelin 
ts_nbus_remove(struct platform_device * pdev)3345b143d2aSSebastien Bourdelin static int ts_nbus_remove(struct platform_device *pdev)
3355b143d2aSSebastien Bourdelin {
3365b143d2aSSebastien Bourdelin 	struct ts_nbus *ts_nbus = dev_get_drvdata(&pdev->dev);
3375b143d2aSSebastien Bourdelin 
3385b143d2aSSebastien Bourdelin 	/* shutdown the FPGA */
3395b143d2aSSebastien Bourdelin 	mutex_lock(&ts_nbus->lock);
3405b143d2aSSebastien Bourdelin 	pwm_disable(ts_nbus->pwm);
3415b143d2aSSebastien Bourdelin 	mutex_unlock(&ts_nbus->lock);
3425b143d2aSSebastien Bourdelin 
3435b143d2aSSebastien Bourdelin 	return 0;
3445b143d2aSSebastien Bourdelin }
3455b143d2aSSebastien Bourdelin 
3465b143d2aSSebastien Bourdelin static const struct of_device_id ts_nbus_of_match[] = {
3475b143d2aSSebastien Bourdelin 	{ .compatible = "technologic,ts-nbus", },
3485b143d2aSSebastien Bourdelin 	{ },
3495b143d2aSSebastien Bourdelin };
3505b143d2aSSebastien Bourdelin MODULE_DEVICE_TABLE(of, ts_nbus_of_match);
3515b143d2aSSebastien Bourdelin 
3525b143d2aSSebastien Bourdelin static struct platform_driver ts_nbus_driver = {
3535b143d2aSSebastien Bourdelin 	.probe		= ts_nbus_probe,
3545b143d2aSSebastien Bourdelin 	.remove		= ts_nbus_remove,
3555b143d2aSSebastien Bourdelin 	.driver		= {
3565b143d2aSSebastien Bourdelin 		.name	= "ts_nbus",
3575b143d2aSSebastien Bourdelin 		.of_match_table = ts_nbus_of_match,
3585b143d2aSSebastien Bourdelin 	},
3595b143d2aSSebastien Bourdelin };
3605b143d2aSSebastien Bourdelin 
3615b143d2aSSebastien Bourdelin module_platform_driver(ts_nbus_driver);
3625b143d2aSSebastien Bourdelin 
3635b143d2aSSebastien Bourdelin MODULE_ALIAS("platform:ts_nbus");
3645b143d2aSSebastien Bourdelin MODULE_AUTHOR("Sebastien Bourdelin <sebastien.bourdelin@savoirfairelinux.com>");
3655b143d2aSSebastien Bourdelin MODULE_DESCRIPTION("Technologic Systems NBUS");
3665b143d2aSSebastien Bourdelin MODULE_LICENSE("GPL v2");
367