xref: /linux/drivers/bus/ts-nbus.c (revision c771600c6af14749609b49565ffb4cac2959710d)
13bb16560SThomas 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)42a04a7da3SUwe Kleine-König static int ts_nbus_init_pdata(struct platform_device *pdev,
43a04a7da3SUwe Kleine-König 			      struct ts_nbus *ts_nbus)
445b143d2aSSebastien Bourdelin {
455b143d2aSSebastien Bourdelin 	ts_nbus->data = devm_gpiod_get_array(&pdev->dev, "ts,data",
465b143d2aSSebastien Bourdelin 			GPIOD_OUT_HIGH);
47a04a7da3SUwe Kleine-König 	if (IS_ERR(ts_nbus->data))
48a04a7da3SUwe Kleine-König 		return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->data),
49a04a7da3SUwe Kleine-König 				     "failed to retrieve ts,data-gpio from dts\n");
505b143d2aSSebastien Bourdelin 
515b143d2aSSebastien Bourdelin 	ts_nbus->csn = devm_gpiod_get(&pdev->dev, "ts,csn", GPIOD_OUT_HIGH);
52a04a7da3SUwe Kleine-König 	if (IS_ERR(ts_nbus->csn))
53a04a7da3SUwe Kleine-König 		return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->csn),
54a04a7da3SUwe Kleine-König 			      "failed to retrieve ts,csn-gpio from dts\n");
555b143d2aSSebastien Bourdelin 
565b143d2aSSebastien Bourdelin 	ts_nbus->txrx = devm_gpiod_get(&pdev->dev, "ts,txrx", GPIOD_OUT_HIGH);
57a04a7da3SUwe Kleine-König 	if (IS_ERR(ts_nbus->txrx))
58a04a7da3SUwe Kleine-König 		return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->txrx),
59a04a7da3SUwe Kleine-König 				     "failed to retrieve ts,txrx-gpio from dts\n");
605b143d2aSSebastien Bourdelin 
615b143d2aSSebastien Bourdelin 	ts_nbus->strobe = devm_gpiod_get(&pdev->dev, "ts,strobe", GPIOD_OUT_HIGH);
62a04a7da3SUwe Kleine-König 	if (IS_ERR(ts_nbus->strobe))
63a04a7da3SUwe Kleine-König 		return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->strobe),
64a04a7da3SUwe Kleine-König 				     "failed to retrieve ts,strobe-gpio from dts\n");
655b143d2aSSebastien Bourdelin 
665b143d2aSSebastien Bourdelin 	ts_nbus->ale = devm_gpiod_get(&pdev->dev, "ts,ale", GPIOD_OUT_HIGH);
67a04a7da3SUwe Kleine-König 	if (IS_ERR(ts_nbus->ale))
68a04a7da3SUwe Kleine-König 		return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->ale),
69a04a7da3SUwe Kleine-König 				     "failed to retrieve ts,ale-gpio from dts\n");
705b143d2aSSebastien Bourdelin 
715b143d2aSSebastien Bourdelin 	ts_nbus->rdy = devm_gpiod_get(&pdev->dev, "ts,rdy", GPIOD_IN);
72a04a7da3SUwe Kleine-König 	if (IS_ERR(ts_nbus->rdy))
73a04a7da3SUwe Kleine-König 		return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->rdy),
74a04a7da3SUwe Kleine-König 				     "failed to retrieve ts,rdy-gpio from dts\n");
755b143d2aSSebastien Bourdelin 
765b143d2aSSebastien Bourdelin 	return 0;
775b143d2aSSebastien Bourdelin }
785b143d2aSSebastien Bourdelin 
795b143d2aSSebastien Bourdelin /*
805b143d2aSSebastien Bourdelin  * the data gpios are used for reading and writing values, their directions
815b143d2aSSebastien Bourdelin  * should be adjusted accordingly.
825b143d2aSSebastien Bourdelin  */
ts_nbus_set_direction(struct ts_nbus * ts_nbus,int direction)835b143d2aSSebastien Bourdelin static void ts_nbus_set_direction(struct ts_nbus *ts_nbus, int direction)
845b143d2aSSebastien Bourdelin {
855b143d2aSSebastien Bourdelin 	int i;
865b143d2aSSebastien Bourdelin 
875b143d2aSSebastien Bourdelin 	for (i = 0; i < 8; i++) {
885b143d2aSSebastien Bourdelin 		if (direction == TS_NBUS_DIRECTION_IN)
895b143d2aSSebastien Bourdelin 			gpiod_direction_input(ts_nbus->data->desc[i]);
905b143d2aSSebastien Bourdelin 		else
915b143d2aSSebastien Bourdelin 			/* when used as output the default state of the data
925b143d2aSSebastien Bourdelin 			 * lines are set to high */
935b143d2aSSebastien Bourdelin 			gpiod_direction_output(ts_nbus->data->desc[i], 1);
945b143d2aSSebastien Bourdelin 	}
955b143d2aSSebastien Bourdelin }
965b143d2aSSebastien Bourdelin 
975b143d2aSSebastien Bourdelin /*
985b143d2aSSebastien Bourdelin  * reset the bus in its initial state.
995b143d2aSSebastien Bourdelin  * The data, csn, strobe and ale lines must be zero'ed to let the FPGA knows a
1005b143d2aSSebastien Bourdelin  * new transaction can be process.
1015b143d2aSSebastien Bourdelin  */
ts_nbus_reset_bus(struct ts_nbus * ts_nbus)1025b143d2aSSebastien Bourdelin static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus)
1035b143d2aSSebastien Bourdelin {
104b9762bebSJanusz Krzysztofik 	DECLARE_BITMAP(values, 8);
1055b143d2aSSebastien Bourdelin 
106b9762bebSJanusz Krzysztofik 	values[0] = 0;
1075b143d2aSSebastien Bourdelin 
10877588c14SJanusz Krzysztofik 	gpiod_set_array_value_cansleep(8, ts_nbus->data->desc,
10977588c14SJanusz Krzysztofik 				       ts_nbus->data->info, values);
1105b143d2aSSebastien Bourdelin 	gpiod_set_value_cansleep(ts_nbus->csn, 0);
1115b143d2aSSebastien Bourdelin 	gpiod_set_value_cansleep(ts_nbus->strobe, 0);
1125b143d2aSSebastien Bourdelin 	gpiod_set_value_cansleep(ts_nbus->ale, 0);
1135b143d2aSSebastien Bourdelin }
1145b143d2aSSebastien Bourdelin 
1155b143d2aSSebastien Bourdelin /*
1165b143d2aSSebastien Bourdelin  * let the FPGA knows it can process.
1175b143d2aSSebastien Bourdelin  */
ts_nbus_start_transaction(struct ts_nbus * ts_nbus)1185b143d2aSSebastien Bourdelin static void ts_nbus_start_transaction(struct ts_nbus *ts_nbus)
1195b143d2aSSebastien Bourdelin {
1205b143d2aSSebastien Bourdelin 	gpiod_set_value_cansleep(ts_nbus->strobe, 1);
1215b143d2aSSebastien Bourdelin }
1225b143d2aSSebastien Bourdelin 
1235b143d2aSSebastien Bourdelin /*
1245b143d2aSSebastien Bourdelin  * read a byte value from the data gpios.
1255b143d2aSSebastien Bourdelin  * return 0 on success or negative errno on failure.
1265b143d2aSSebastien Bourdelin  */
ts_nbus_read_byte(struct ts_nbus * ts_nbus,u8 * val)1275b143d2aSSebastien Bourdelin static int ts_nbus_read_byte(struct ts_nbus *ts_nbus, u8 *val)
1285b143d2aSSebastien Bourdelin {
1295b143d2aSSebastien Bourdelin 	struct gpio_descs *gpios = ts_nbus->data;
1305b143d2aSSebastien Bourdelin 	int ret, i;
1315b143d2aSSebastien Bourdelin 
1325b143d2aSSebastien Bourdelin 	*val = 0;
1335b143d2aSSebastien Bourdelin 	for (i = 0; i < 8; i++) {
1345b143d2aSSebastien Bourdelin 		ret = gpiod_get_value_cansleep(gpios->desc[i]);
1355b143d2aSSebastien Bourdelin 		if (ret < 0)
1365b143d2aSSebastien Bourdelin 			return ret;
1375b143d2aSSebastien Bourdelin 		if (ret)
1385b143d2aSSebastien Bourdelin 			*val |= BIT(i);
1395b143d2aSSebastien Bourdelin 	}
1405b143d2aSSebastien Bourdelin 
1415b143d2aSSebastien Bourdelin 	return 0;
1425b143d2aSSebastien Bourdelin }
1435b143d2aSSebastien Bourdelin 
1445b143d2aSSebastien Bourdelin /*
1455b143d2aSSebastien Bourdelin  * set the data gpios accordingly to the byte value.
1465b143d2aSSebastien Bourdelin  */
ts_nbus_write_byte(struct ts_nbus * ts_nbus,u8 byte)1475b143d2aSSebastien Bourdelin static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte)
1485b143d2aSSebastien Bourdelin {
1495b143d2aSSebastien Bourdelin 	struct gpio_descs *gpios = ts_nbus->data;
150b9762bebSJanusz Krzysztofik 	DECLARE_BITMAP(values, 8);
1515b143d2aSSebastien Bourdelin 
152b9762bebSJanusz Krzysztofik 	values[0] = byte;
1535b143d2aSSebastien Bourdelin 
15477588c14SJanusz Krzysztofik 	gpiod_set_array_value_cansleep(8, gpios->desc, gpios->info, values);
1555b143d2aSSebastien Bourdelin }
1565b143d2aSSebastien Bourdelin 
1575b143d2aSSebastien Bourdelin /*
1585b143d2aSSebastien Bourdelin  * reading the bus consists of resetting the bus, then notifying the FPGA to
1595b143d2aSSebastien Bourdelin  * send the data in the data gpios and return the read value.
1605b143d2aSSebastien Bourdelin  * return 0 on success or negative errno on failure.
1615b143d2aSSebastien Bourdelin  */
ts_nbus_read_bus(struct ts_nbus * ts_nbus,u8 * val)1625b143d2aSSebastien Bourdelin static int ts_nbus_read_bus(struct ts_nbus *ts_nbus, u8 *val)
1635b143d2aSSebastien Bourdelin {
1645b143d2aSSebastien Bourdelin 	ts_nbus_reset_bus(ts_nbus);
1655b143d2aSSebastien Bourdelin 	ts_nbus_start_transaction(ts_nbus);
1665b143d2aSSebastien Bourdelin 
1675b143d2aSSebastien Bourdelin 	return ts_nbus_read_byte(ts_nbus, val);
1685b143d2aSSebastien Bourdelin }
1695b143d2aSSebastien Bourdelin 
1705b143d2aSSebastien Bourdelin /*
1715b143d2aSSebastien Bourdelin  * writing to the bus consists of resetting the bus, then define the type of
1725b143d2aSSebastien Bourdelin  * command (address/value), write the data and notify the FPGA to retrieve the
1735b143d2aSSebastien Bourdelin  * value in the data gpios.
1745b143d2aSSebastien Bourdelin  */
ts_nbus_write_bus(struct ts_nbus * ts_nbus,int cmd,u8 val)1755b143d2aSSebastien Bourdelin static void ts_nbus_write_bus(struct ts_nbus *ts_nbus, int cmd, u8 val)
1765b143d2aSSebastien Bourdelin {
1775b143d2aSSebastien Bourdelin 	ts_nbus_reset_bus(ts_nbus);
1785b143d2aSSebastien Bourdelin 
1795b143d2aSSebastien Bourdelin 	if (cmd == TS_NBUS_WRITE_ADR)
1805b143d2aSSebastien Bourdelin 		gpiod_set_value_cansleep(ts_nbus->ale, 1);
1815b143d2aSSebastien Bourdelin 
1825b143d2aSSebastien Bourdelin 	ts_nbus_write_byte(ts_nbus, val);
1835b143d2aSSebastien Bourdelin 	ts_nbus_start_transaction(ts_nbus);
1845b143d2aSSebastien Bourdelin }
1855b143d2aSSebastien Bourdelin 
1865b143d2aSSebastien Bourdelin /*
1875b143d2aSSebastien Bourdelin  * read the value in the FPGA register at the given address.
1885b143d2aSSebastien Bourdelin  * return 0 on success or negative errno on failure.
1895b143d2aSSebastien Bourdelin  */
ts_nbus_read(struct ts_nbus * ts_nbus,u8 adr,u16 * val)1905b143d2aSSebastien Bourdelin int ts_nbus_read(struct ts_nbus *ts_nbus, u8 adr, u16 *val)
1915b143d2aSSebastien Bourdelin {
1925b143d2aSSebastien Bourdelin 	int ret, i;
1935b143d2aSSebastien Bourdelin 	u8 byte;
1945b143d2aSSebastien Bourdelin 
1955b143d2aSSebastien Bourdelin 	/* bus access must be atomic */
1965b143d2aSSebastien Bourdelin 	mutex_lock(&ts_nbus->lock);
1975b143d2aSSebastien Bourdelin 
1985b143d2aSSebastien Bourdelin 	/* set the bus in read mode */
1995b143d2aSSebastien Bourdelin 	gpiod_set_value_cansleep(ts_nbus->txrx, 0);
2005b143d2aSSebastien Bourdelin 
2015b143d2aSSebastien Bourdelin 	/* write address */
2025b143d2aSSebastien Bourdelin 	ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_ADR, adr);
2035b143d2aSSebastien Bourdelin 
2045b143d2aSSebastien Bourdelin 	/* set the data gpios direction as input before reading */
2055b143d2aSSebastien Bourdelin 	ts_nbus_set_direction(ts_nbus, TS_NBUS_DIRECTION_IN);
2065b143d2aSSebastien Bourdelin 
2075b143d2aSSebastien Bourdelin 	/* reading value MSB first */
2085b143d2aSSebastien Bourdelin 	do {
2095b143d2aSSebastien Bourdelin 		*val = 0;
2105b143d2aSSebastien Bourdelin 		byte = 0;
2115b143d2aSSebastien Bourdelin 		for (i = 1; i >= 0; i--) {
2125b143d2aSSebastien Bourdelin 			/* read a byte from the bus, leave on error */
2135b143d2aSSebastien Bourdelin 			ret = ts_nbus_read_bus(ts_nbus, &byte);
2145b143d2aSSebastien Bourdelin 			if (ret < 0)
2155b143d2aSSebastien Bourdelin 				goto err;
2165b143d2aSSebastien Bourdelin 
2175b143d2aSSebastien Bourdelin 			/* append the byte read to the final value */
2185b143d2aSSebastien Bourdelin 			*val |= byte << (i * 8);
2195b143d2aSSebastien Bourdelin 		}
2205b143d2aSSebastien Bourdelin 		gpiod_set_value_cansleep(ts_nbus->csn, 1);
2215b143d2aSSebastien Bourdelin 		ret = gpiod_get_value_cansleep(ts_nbus->rdy);
2225b143d2aSSebastien Bourdelin 	} while (ret);
2235b143d2aSSebastien Bourdelin 
2245b143d2aSSebastien Bourdelin err:
2255b143d2aSSebastien Bourdelin 	/* restore the data gpios direction as output after reading */
2265b143d2aSSebastien Bourdelin 	ts_nbus_set_direction(ts_nbus, TS_NBUS_DIRECTION_OUT);
2275b143d2aSSebastien Bourdelin 
2285b143d2aSSebastien Bourdelin 	mutex_unlock(&ts_nbus->lock);
2295b143d2aSSebastien Bourdelin 
2305b143d2aSSebastien Bourdelin 	return ret;
2315b143d2aSSebastien Bourdelin }
2325b143d2aSSebastien Bourdelin EXPORT_SYMBOL_GPL(ts_nbus_read);
2335b143d2aSSebastien Bourdelin 
2345b143d2aSSebastien Bourdelin /*
2355b143d2aSSebastien Bourdelin  * write the desired value in the FPGA register at the given address.
2365b143d2aSSebastien Bourdelin  */
ts_nbus_write(struct ts_nbus * ts_nbus,u8 adr,u16 val)2375b143d2aSSebastien Bourdelin int ts_nbus_write(struct ts_nbus *ts_nbus, u8 adr, u16 val)
2385b143d2aSSebastien Bourdelin {
2395b143d2aSSebastien Bourdelin 	int i;
2405b143d2aSSebastien Bourdelin 
2415b143d2aSSebastien Bourdelin 	/* bus access must be atomic */
2425b143d2aSSebastien Bourdelin 	mutex_lock(&ts_nbus->lock);
2435b143d2aSSebastien Bourdelin 
2445b143d2aSSebastien Bourdelin 	/* set the bus in write mode */
2455b143d2aSSebastien Bourdelin 	gpiod_set_value_cansleep(ts_nbus->txrx, 1);
2465b143d2aSSebastien Bourdelin 
2475b143d2aSSebastien Bourdelin 	/* write address */
2485b143d2aSSebastien Bourdelin 	ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_ADR, adr);
2495b143d2aSSebastien Bourdelin 
2505b143d2aSSebastien Bourdelin 	/* writing value MSB first */
2515b143d2aSSebastien Bourdelin 	for (i = 1; i >= 0; i--)
2525b143d2aSSebastien Bourdelin 		ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_VAL, (u8)(val >> (i * 8)));
2535b143d2aSSebastien Bourdelin 
2545b143d2aSSebastien Bourdelin 	/* wait for completion */
2555b143d2aSSebastien Bourdelin 	gpiod_set_value_cansleep(ts_nbus->csn, 1);
2565b143d2aSSebastien Bourdelin 	while (gpiod_get_value_cansleep(ts_nbus->rdy) != 0) {
2575b143d2aSSebastien Bourdelin 		gpiod_set_value_cansleep(ts_nbus->csn, 0);
2585b143d2aSSebastien Bourdelin 		gpiod_set_value_cansleep(ts_nbus->csn, 1);
2595b143d2aSSebastien Bourdelin 	}
2605b143d2aSSebastien Bourdelin 
2615b143d2aSSebastien Bourdelin 	mutex_unlock(&ts_nbus->lock);
2625b143d2aSSebastien Bourdelin 
2635b143d2aSSebastien Bourdelin 	return 0;
2645b143d2aSSebastien Bourdelin }
2655b143d2aSSebastien Bourdelin EXPORT_SYMBOL_GPL(ts_nbus_write);
2665b143d2aSSebastien Bourdelin 
ts_nbus_probe(struct platform_device * pdev)2675b143d2aSSebastien Bourdelin static int ts_nbus_probe(struct platform_device *pdev)
2685b143d2aSSebastien Bourdelin {
2695b143d2aSSebastien Bourdelin 	struct pwm_device *pwm;
2708129d25eSUwe Kleine-König 	struct pwm_state state;
2715b143d2aSSebastien Bourdelin 	struct device *dev = &pdev->dev;
2725b143d2aSSebastien Bourdelin 	struct ts_nbus *ts_nbus;
2735b143d2aSSebastien Bourdelin 	int ret;
2745b143d2aSSebastien Bourdelin 
2755b143d2aSSebastien Bourdelin 	ts_nbus = devm_kzalloc(dev, sizeof(*ts_nbus), GFP_KERNEL);
2765b143d2aSSebastien Bourdelin 	if (!ts_nbus)
2775b143d2aSSebastien Bourdelin 		return -ENOMEM;
2785b143d2aSSebastien Bourdelin 
2795b143d2aSSebastien Bourdelin 	mutex_init(&ts_nbus->lock);
2805b143d2aSSebastien Bourdelin 
2815b143d2aSSebastien Bourdelin 	ret = ts_nbus_init_pdata(pdev, ts_nbus);
2825b143d2aSSebastien Bourdelin 	if (ret < 0)
2835b143d2aSSebastien Bourdelin 		return ret;
2845b143d2aSSebastien Bourdelin 
2855b143d2aSSebastien Bourdelin 	pwm = devm_pwm_get(dev, NULL);
286a04a7da3SUwe Kleine-König 	if (IS_ERR(pwm))
287a04a7da3SUwe Kleine-König 		return dev_err_probe(dev, PTR_ERR(pwm),
288a04a7da3SUwe Kleine-König 				     "unable to request PWM\n");
2895b143d2aSSebastien Bourdelin 
2908129d25eSUwe Kleine-König 	pwm_init_state(pwm, &state);
291a04a7da3SUwe Kleine-König 	if (!state.period)
292a04a7da3SUwe Kleine-König 		return dev_err_probe(dev, -EINVAL, "invalid PWM period\n");
2935b143d2aSSebastien Bourdelin 
2948129d25eSUwe Kleine-König 	state.duty_cycle = state.period;
2958129d25eSUwe Kleine-König 	state.enabled = true;
2968129d25eSUwe Kleine-König 
297f8b03e5cSSean Young 	ret = pwm_apply_might_sleep(pwm, &state);
2985b143d2aSSebastien Bourdelin 	if (ret < 0)
299a04a7da3SUwe Kleine-König 		return dev_err_probe(dev, ret, "failed to configure PWM\n");
3005b143d2aSSebastien Bourdelin 
3015b143d2aSSebastien Bourdelin 	/*
3025b143d2aSSebastien Bourdelin 	 * we can now start the FPGA and populate the peripherals.
3035b143d2aSSebastien Bourdelin 	 */
3045b143d2aSSebastien Bourdelin 	ts_nbus->pwm = pwm;
3055b143d2aSSebastien Bourdelin 
3065b143d2aSSebastien Bourdelin 	/*
3075b143d2aSSebastien Bourdelin 	 * let the child nodes retrieve this instance of the ts-nbus.
3085b143d2aSSebastien Bourdelin 	 */
3095b143d2aSSebastien Bourdelin 	dev_set_drvdata(dev, ts_nbus);
3105b143d2aSSebastien Bourdelin 
3115b143d2aSSebastien Bourdelin 	ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
3125b143d2aSSebastien Bourdelin 	if (ret < 0)
313a04a7da3SUwe Kleine-König 		return dev_err_probe(dev, ret,
314a04a7da3SUwe Kleine-König 				     "failed to populate platform devices on bus\n");
3155b143d2aSSebastien Bourdelin 
3165b143d2aSSebastien Bourdelin 	dev_info(dev, "initialized\n");
3175b143d2aSSebastien Bourdelin 
3185b143d2aSSebastien Bourdelin 	return 0;
3195b143d2aSSebastien Bourdelin }
3205b143d2aSSebastien Bourdelin 
ts_nbus_remove(struct platform_device * pdev)321fc540426SUwe Kleine-König static void ts_nbus_remove(struct platform_device *pdev)
3225b143d2aSSebastien Bourdelin {
3235b143d2aSSebastien Bourdelin 	struct ts_nbus *ts_nbus = dev_get_drvdata(&pdev->dev);
3245b143d2aSSebastien Bourdelin 
3255b143d2aSSebastien Bourdelin 	/* shutdown the FPGA */
3265b143d2aSSebastien Bourdelin 	mutex_lock(&ts_nbus->lock);
3275b143d2aSSebastien Bourdelin 	pwm_disable(ts_nbus->pwm);
3285b143d2aSSebastien Bourdelin 	mutex_unlock(&ts_nbus->lock);
3295b143d2aSSebastien Bourdelin }
3305b143d2aSSebastien Bourdelin 
3315b143d2aSSebastien Bourdelin static const struct of_device_id ts_nbus_of_match[] = {
3325b143d2aSSebastien Bourdelin 	{ .compatible = "technologic,ts-nbus", },
3335b143d2aSSebastien Bourdelin 	{ },
3345b143d2aSSebastien Bourdelin };
3355b143d2aSSebastien Bourdelin MODULE_DEVICE_TABLE(of, ts_nbus_of_match);
3365b143d2aSSebastien Bourdelin 
3375b143d2aSSebastien Bourdelin static struct platform_driver ts_nbus_driver = {
3385b143d2aSSebastien Bourdelin 	.probe		= ts_nbus_probe,
339*1fac9f8bSUwe Kleine-König 	.remove		= ts_nbus_remove,
3405b143d2aSSebastien Bourdelin 	.driver		= {
3415b143d2aSSebastien Bourdelin 		.name	= "ts_nbus",
3425b143d2aSSebastien Bourdelin 		.of_match_table = ts_nbus_of_match,
3435b143d2aSSebastien Bourdelin 	},
3445b143d2aSSebastien Bourdelin };
3455b143d2aSSebastien Bourdelin 
3465b143d2aSSebastien Bourdelin module_platform_driver(ts_nbus_driver);
3475b143d2aSSebastien Bourdelin 
3485b143d2aSSebastien Bourdelin MODULE_ALIAS("platform:ts_nbus");
3495b143d2aSSebastien Bourdelin MODULE_AUTHOR("Sebastien Bourdelin <sebastien.bourdelin@savoirfairelinux.com>");
3505b143d2aSSebastien Bourdelin MODULE_DESCRIPTION("Technologic Systems NBUS");
3515b143d2aSSebastien Bourdelin MODULE_LICENSE("GPL v2");
352