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