* TSC2005 touchscreen driver
*
* Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2015 QWERTY Embedded Design
+ * Copyright (C) 2015 EMAC Inc.
*
- * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
- * based on TSC2301 driver by Klaus K. Pedersen <klaus.k.pedersen@nokia.com>
+ * Based on original tsc2005.c by Lauri Leukkunen <lauri.leukkunen@nokia.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/input.h>
-#include <linux/input/touchscreen.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/pm.h>
-#include <linux/of.h>
#include <linux/spi/spi.h>
-#include <linux/spi/tsc2005.h>
-#include <linux/regulator/consumer.h>
#include <linux/regmap.h>
-#include <linux/gpio/consumer.h>
-
-/*
- * The touchscreen interface operates as follows:
- *
- * 1) Pen is pressed against the touchscreen.
- * 2) TSC2005 performs AD conversion.
- * 3) After the conversion is done TSC2005 drives DAV line down.
- * 4) GPIO IRQ is received and tsc2005_irq_thread() is scheduled.
- * 5) tsc2005_irq_thread() queues up an spi transfer to fetch the x, y, z1, z2
- * values.
- * 6) tsc2005_irq_thread() reports coordinates to input layer and sets up
- * tsc2005_penup_timer() to be called after TSC2005_PENUP_TIME_MS (40ms).
- * 7) When the penup timer expires, there have not been touch or DAV interrupts
- * during the last 40ms which means the pen has been lifted.
- *
- * ESD recovery via a hardware reset is done if the TSC2005 doesn't respond
- * after a configurable period (in ms) of activity. If esd_timeout is 0, the
- * watchdog is disabled.
- */
-
-/* control byte 1 */
-#define TSC2005_CMD 0x80
-#define TSC2005_CMD_NORMAL 0x00
-#define TSC2005_CMD_STOP 0x01
-#define TSC2005_CMD_12BIT 0x04
-
-/* control byte 0 */
-#define TSC2005_REG_READ 0x01 /* R/W access */
-#define TSC2005_REG_PND0 0x02 /* Power Not Down Control */
-#define TSC2005_REG_X (0x0 << 3)
-#define TSC2005_REG_Y (0x1 << 3)
-#define TSC2005_REG_Z1 (0x2 << 3)
-#define TSC2005_REG_Z2 (0x3 << 3)
-#define TSC2005_REG_AUX (0x4 << 3)
-#define TSC2005_REG_TEMP1 (0x5 << 3)
-#define TSC2005_REG_TEMP2 (0x6 << 3)
-#define TSC2005_REG_STATUS (0x7 << 3)
-#define TSC2005_REG_AUX_HIGH (0x8 << 3)
-#define TSC2005_REG_AUX_LOW (0x9 << 3)
-#define TSC2005_REG_TEMP_HIGH (0xA << 3)
-#define TSC2005_REG_TEMP_LOW (0xB << 3)
-#define TSC2005_REG_CFR0 (0xC << 3)
-#define TSC2005_REG_CFR1 (0xD << 3)
-#define TSC2005_REG_CFR2 (0xE << 3)
-#define TSC2005_REG_CONV_FUNC (0xF << 3)
-
-/* configuration register 0 */
-#define TSC2005_CFR0_PRECHARGE_276US 0x0040
-#define TSC2005_CFR0_STABTIME_1MS 0x0300
-#define TSC2005_CFR0_CLOCK_1MHZ 0x1000
-#define TSC2005_CFR0_RESOLUTION12 0x2000
-#define TSC2005_CFR0_PENMODE 0x8000
-#define TSC2005_CFR0_INITVALUE (TSC2005_CFR0_STABTIME_1MS | \
- TSC2005_CFR0_CLOCK_1MHZ | \
- TSC2005_CFR0_RESOLUTION12 | \
- TSC2005_CFR0_PRECHARGE_276US | \
- TSC2005_CFR0_PENMODE)
-
-/* bits common to both read and write of configuration register 0 */
-#define TSC2005_CFR0_RW_MASK 0x3fff
-
-/* configuration register 1 */
-#define TSC2005_CFR1_BATCHDELAY_4MS 0x0003
-#define TSC2005_CFR1_INITVALUE TSC2005_CFR1_BATCHDELAY_4MS
-
-/* configuration register 2 */
-#define TSC2005_CFR2_MAVE_Z 0x0004
-#define TSC2005_CFR2_MAVE_Y 0x0008
-#define TSC2005_CFR2_MAVE_X 0x0010
-#define TSC2005_CFR2_AVG_7 0x0800
-#define TSC2005_CFR2_MEDIUM_15 0x3000
-#define TSC2005_CFR2_INITVALUE (TSC2005_CFR2_MAVE_X | \
- TSC2005_CFR2_MAVE_Y | \
- TSC2005_CFR2_MAVE_Z | \
- TSC2005_CFR2_MEDIUM_15 | \
- TSC2005_CFR2_AVG_7)
-
-#define MAX_12BIT 0xfff
-#define TSC2005_DEF_X_FUZZ 4
-#define TSC2005_DEF_Y_FUZZ 8
-#define TSC2005_DEF_P_FUZZ 2
-#define TSC2005_DEF_RESISTOR 280
-
-#define TSC2005_SPI_MAX_SPEED_HZ 10000000
-#define TSC2005_PENUP_TIME_MS 40
-
-static const struct regmap_range tsc2005_writable_ranges[] = {
- regmap_reg_range(TSC2005_REG_AUX_HIGH, TSC2005_REG_CFR2),
-};
-
-static const struct regmap_access_table tsc2005_writable_table = {
- .yes_ranges = tsc2005_writable_ranges,
- .n_yes_ranges = ARRAY_SIZE(tsc2005_writable_ranges),
-};
-
-static struct regmap_config tsc2005_regmap_config = {
- .reg_bits = 8,
- .val_bits = 16,
- .reg_stride = 0x08,
- .max_register = 0x78,
- .read_flag_mask = TSC2005_REG_READ,
- .write_flag_mask = TSC2005_REG_PND0,
- .wr_table = &tsc2005_writable_table,
- .use_single_rw = true,
-};
-
-struct tsc2005_data {
- u16 x;
- u16 y;
- u16 z1;
- u16 z2;
-} __packed;
-#define TSC2005_DATA_REGS 4
-
-struct tsc2005 {
- struct spi_device *spi;
- struct regmap *regmap;
-
- struct input_dev *idev;
- char phys[32];
-
- struct mutex mutex;
-
- /* raw copy of previous x,y,z */
- int in_x;
- int in_y;
- int in_z1;
- int in_z2;
-
- spinlock_t lock;
- struct timer_list penup_timer;
+#include "tsc200x-core.h"
- unsigned int esd_timeout;
- struct delayed_work esd_work;
- unsigned long last_valid_interrupt;
-
- unsigned int x_plate_ohm;
-
- bool opened;
- bool suspended;
-
- bool pen_down;
-
- struct regulator *vio;
-
- struct gpio_desc *reset_gpio;
- void (*set_reset)(bool enable);
-};
-
-static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd)
+static int tsc2005_cmd(struct device *dev, u8 cmd)
{
- u8 tx = TSC2005_CMD | TSC2005_CMD_12BIT | cmd;
+ u8 tx = TSC200X_CMD | TSC200X_CMD_12BIT | cmd;
struct spi_transfer xfer = {
- .tx_buf = &tx,
- .len = 1,
- .bits_per_word = 8,
+ .tx_buf = &tx,
+ .len = 1,
+ .bits_per_word = 8,
};
struct spi_message msg;
+ struct spi_device *spi = to_spi_device(dev);
int error;
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
- error = spi_sync(ts->spi, &msg);
+ error = spi_sync(spi, &msg);
if (error) {
- dev_err(&ts->spi->dev, "%s: failed, command: %x, error: %d\n",
+ dev_err(dev, "%s: failed, command: %x, spi error: %d\n",
__func__, cmd, error);
return error;
}
return 0;
}
-static void tsc2005_update_pen_state(struct tsc2005 *ts,
- int x, int y, int pressure)
-{
- if (pressure) {
- input_report_abs(ts->idev, ABS_X, x);
- input_report_abs(ts->idev, ABS_Y, y);
- input_report_abs(ts->idev, ABS_PRESSURE, pressure);
- if (!ts->pen_down) {
- input_report_key(ts->idev, BTN_TOUCH, !!pressure);
- ts->pen_down = true;
- }
- } else {
- input_report_abs(ts->idev, ABS_PRESSURE, 0);
- if (ts->pen_down) {
- input_report_key(ts->idev, BTN_TOUCH, 0);
- ts->pen_down = false;
- }
- }
- input_sync(ts->idev);
- dev_dbg(&ts->spi->dev, "point(%4d,%4d), pressure (%4d)\n", x, y,
- pressure);
-}
-
-static irqreturn_t tsc2005_irq_thread(int irq, void *_ts)
-{
- struct tsc2005 *ts = _ts;
- unsigned long flags;
- unsigned int pressure;
- struct tsc2005_data tsdata;
- int error;
-
- /* read the coordinates */
- error = regmap_bulk_read(ts->regmap, TSC2005_REG_X, &tsdata,
- TSC2005_DATA_REGS);
- if (unlikely(error))
- goto out;
-
- /* validate position */
- if (unlikely(tsdata.x > MAX_12BIT || tsdata.y > MAX_12BIT))
- goto out;
-
- /* Skip reading if the pressure components are out of range */
- if (unlikely(tsdata.z1 == 0 || tsdata.z2 > MAX_12BIT))
- goto out;
- if (unlikely(tsdata.z1 >= tsdata.z2))
- goto out;
-
- /*
- * Skip point if this is a pen down with the exact same values as
- * the value before pen-up - that implies SPI fed us stale data
- */
- if (!ts->pen_down &&
- ts->in_x == tsdata.x && ts->in_y == tsdata.y &&
- ts->in_z1 == tsdata.z1 && ts->in_z2 == tsdata.z2) {
- goto out;
- }
-
- /*
- * At this point we are happy we have a valid and useful reading.
- * Remember it for later comparisons. We may now begin downsampling.
- */
- ts->in_x = tsdata.x;
- ts->in_y = tsdata.y;
- ts->in_z1 = tsdata.z1;
- ts->in_z2 = tsdata.z2;
-
- /* Compute touch pressure resistance using equation #1 */
- pressure = tsdata.x * (tsdata.z2 - tsdata.z1) / tsdata.z1;
- pressure = pressure * ts->x_plate_ohm / 4096;
- if (unlikely(pressure > MAX_12BIT))
- goto out;
-
- spin_lock_irqsave(&ts->lock, flags);
-
- tsc2005_update_pen_state(ts, tsdata.x, tsdata.y, pressure);
- mod_timer(&ts->penup_timer,
- jiffies + msecs_to_jiffies(TSC2005_PENUP_TIME_MS));
-
- spin_unlock_irqrestore(&ts->lock, flags);
-
- ts->last_valid_interrupt = jiffies;
-out:
- return IRQ_HANDLED;
-}
-
-static void tsc2005_penup_timer(unsigned long data)
-{
- struct tsc2005 *ts = (struct tsc2005 *)data;
- unsigned long flags;
-
- spin_lock_irqsave(&ts->lock, flags);
- tsc2005_update_pen_state(ts, 0, 0, 0);
- spin_unlock_irqrestore(&ts->lock, flags);
-}
-
-static void tsc2005_start_scan(struct tsc2005 *ts)
-{
- regmap_write(ts->regmap, TSC2005_REG_CFR0, TSC2005_CFR0_INITVALUE);
- regmap_write(ts->regmap, TSC2005_REG_CFR1, TSC2005_CFR1_INITVALUE);
- regmap_write(ts->regmap, TSC2005_REG_CFR2, TSC2005_CFR2_INITVALUE);
- tsc2005_cmd(ts, TSC2005_CMD_NORMAL);
-}
-
-static void tsc2005_stop_scan(struct tsc2005 *ts)
-{
- tsc2005_cmd(ts, TSC2005_CMD_STOP);
-}
-
-static void tsc2005_set_reset(struct tsc2005 *ts, bool enable)
-{
- if (ts->reset_gpio)
- gpiod_set_value_cansleep(ts->reset_gpio, enable);
- else if (ts->set_reset)
- ts->set_reset(enable);
-}
-
-/* must be called with ts->mutex held */
-static void __tsc2005_disable(struct tsc2005 *ts)
-{
- tsc2005_stop_scan(ts);
-
- disable_irq(ts->spi->irq);
- del_timer_sync(&ts->penup_timer);
-
- cancel_delayed_work_sync(&ts->esd_work);
-
- enable_irq(ts->spi->irq);
-}
-
-/* must be called with ts->mutex held */
-static void __tsc2005_enable(struct tsc2005 *ts)
-{
- tsc2005_start_scan(ts);
-
- if (ts->esd_timeout && (ts->set_reset || ts->reset_gpio)) {
- ts->last_valid_interrupt = jiffies;
- schedule_delayed_work(&ts->esd_work,
- round_jiffies_relative(
- msecs_to_jiffies(ts->esd_timeout)));
- }
-
-}
-
-static ssize_t tsc2005_selftest_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct tsc2005 *ts = dev_get_drvdata(dev);
- unsigned int temp_high;
- unsigned int temp_high_orig;
- unsigned int temp_high_test;
- bool success = true;
- int error;
-
- mutex_lock(&ts->mutex);
-
- /*
- * Test TSC2005 communications via temp high register.
- */
- __tsc2005_disable(ts);
-
- error = regmap_read(ts->regmap, TSC2005_REG_TEMP_HIGH, &temp_high_orig);
- if (error) {
- dev_warn(dev, "selftest failed: read error %d\n", error);
- success = false;
- goto out;
- }
-
- temp_high_test = (temp_high_orig - 1) & MAX_12BIT;
-
- error = regmap_write(ts->regmap, TSC2005_REG_TEMP_HIGH, temp_high_test);
- if (error) {
- dev_warn(dev, "selftest failed: write error %d\n", error);
- success = false;
- goto out;
- }
-
- error = regmap_read(ts->regmap, TSC2005_REG_TEMP_HIGH, &temp_high);
- if (error) {
- dev_warn(dev, "selftest failed: read error %d after write\n",
- error);
- success = false;
- goto out;
- }
-
- if (temp_high != temp_high_test) {
- dev_warn(dev, "selftest failed: %d != %d\n",
- temp_high, temp_high_test);
- success = false;
- }
-
- /* hardware reset */
- tsc2005_set_reset(ts, false);
- usleep_range(100, 500); /* only 10us required */
- tsc2005_set_reset(ts, true);
-
- if (!success)
- goto out;
-
- /* test that the reset really happened */
- error = regmap_read(ts->regmap, TSC2005_REG_TEMP_HIGH, &temp_high);
- if (error) {
- dev_warn(dev, "selftest failed: read error %d after reset\n",
- error);
- success = false;
- goto out;
- }
-
- if (temp_high != temp_high_orig) {
- dev_warn(dev, "selftest failed after reset: %d != %d\n",
- temp_high, temp_high_orig);
- success = false;
- }
-
-out:
- __tsc2005_enable(ts);
- mutex_unlock(&ts->mutex);
-
- return sprintf(buf, "%d\n", success);
-}
-
-static DEVICE_ATTR(selftest, S_IRUGO, tsc2005_selftest_show, NULL);
-
-static struct attribute *tsc2005_attrs[] = {
- &dev_attr_selftest.attr,
- NULL
-};
-
-static umode_t tsc2005_attr_is_visible(struct kobject *kobj,
- struct attribute *attr, int n)
-{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct tsc2005 *ts = dev_get_drvdata(dev);
- umode_t mode = attr->mode;
-
- if (attr == &dev_attr_selftest.attr) {
- if (!ts->set_reset && !ts->reset_gpio)
- mode = 0;
- }
-
- return mode;
-}
-
-static const struct attribute_group tsc2005_attr_group = {
- .is_visible = tsc2005_attr_is_visible,
- .attrs = tsc2005_attrs,
-};
-
-static void tsc2005_esd_work(struct work_struct *work)
-{
- struct tsc2005 *ts = container_of(work, struct tsc2005, esd_work.work);
- int error;
- unsigned int r;
-
- if (!mutex_trylock(&ts->mutex)) {
- /*
- * If the mutex is taken, it means that disable or enable is in
- * progress. In that case just reschedule the work. If the work
- * is not needed, it will be canceled by disable.
- */
- goto reschedule;
- }
-
- if (time_is_after_jiffies(ts->last_valid_interrupt +
- msecs_to_jiffies(ts->esd_timeout)))
- goto out;
-
- /* We should be able to read register without disabling interrupts. */
- error = regmap_read(ts->regmap, TSC2005_REG_CFR0, &r);
- if (!error &&
- !((r ^ TSC2005_CFR0_INITVALUE) & TSC2005_CFR0_RW_MASK)) {
- goto out;
- }
-
- /*
- * If we could not read our known value from configuration register 0
- * then we should reset the controller as if from power-up and start
- * scanning again.
- */
- dev_info(&ts->spi->dev, "TSC2005 not responding - resetting\n");
-
- disable_irq(ts->spi->irq);
- del_timer_sync(&ts->penup_timer);
-
- tsc2005_update_pen_state(ts, 0, 0, 0);
-
- tsc2005_set_reset(ts, false);
- usleep_range(100, 500); /* only 10us required */
- tsc2005_set_reset(ts, true);
-
- enable_irq(ts->spi->irq);
- tsc2005_start_scan(ts);
-
-out:
- mutex_unlock(&ts->mutex);
-reschedule:
- /* re-arm the watchdog */
- schedule_delayed_work(&ts->esd_work,
- round_jiffies_relative(
- msecs_to_jiffies(ts->esd_timeout)));
-}
-
-static int tsc2005_open(struct input_dev *input)
-{
- struct tsc2005 *ts = input_get_drvdata(input);
-
- mutex_lock(&ts->mutex);
-
- if (!ts->suspended)
- __tsc2005_enable(ts);
-
- ts->opened = true;
-
- mutex_unlock(&ts->mutex);
-
- return 0;
-}
-
-static void tsc2005_close(struct input_dev *input)
-{
- struct tsc2005 *ts = input_get_drvdata(input);
-
- mutex_lock(&ts->mutex);
-
- if (!ts->suspended)
- __tsc2005_disable(ts);
-
- ts->opened = false;
-
- mutex_unlock(&ts->mutex);
-}
-
static int tsc2005_probe(struct spi_device *spi)
{
- const struct tsc2005_platform_data *pdata = dev_get_platdata(&spi->dev);
- struct device_node *np = spi->dev.of_node;
-
- struct tsc2005 *ts;
- struct input_dev *input_dev;
- unsigned int max_x = MAX_12BIT;
- unsigned int max_y = MAX_12BIT;
- unsigned int max_p = MAX_12BIT;
- unsigned int fudge_x = TSC2005_DEF_X_FUZZ;
- unsigned int fudge_y = TSC2005_DEF_Y_FUZZ;
- unsigned int fudge_p = TSC2005_DEF_P_FUZZ;
- unsigned int x_plate_ohm = TSC2005_DEF_RESISTOR;
- unsigned int esd_timeout;
int error;
- if (!np && !pdata) {
- dev_err(&spi->dev, "no platform data\n");
- return -ENODEV;
- }
-
- if (spi->irq <= 0) {
- dev_err(&spi->dev, "no irq\n");
- return -ENODEV;
- }
-
- if (pdata) {
- fudge_x = pdata->ts_x_fudge;
- fudge_y = pdata->ts_y_fudge;
- fudge_p = pdata->ts_pressure_fudge;
- max_x = pdata->ts_x_max;
- max_y = pdata->ts_y_max;
- max_p = pdata->ts_pressure_max;
- x_plate_ohm = pdata->ts_x_plate_ohm;
- esd_timeout = pdata->esd_timeout_ms;
- } else {
- x_plate_ohm = TSC2005_DEF_RESISTOR;
- of_property_read_u32(np, "ti,x-plate-ohms", &x_plate_ohm);
- esd_timeout = 0;
- of_property_read_u32(np, "ti,esd-recovery-timeout-ms",
- &esd_timeout);
- }
-
spi->mode = SPI_MODE_0;
spi->bits_per_word = 8;
if (!spi->max_speed_hz)
if (error)
return error;
- ts = devm_kzalloc(&spi->dev, sizeof(*ts), GFP_KERNEL);
- if (!ts)
- return -ENOMEM;
-
- input_dev = devm_input_allocate_device(&spi->dev);
- if (!input_dev)
- return -ENOMEM;
-
- ts->spi = spi;
- ts->idev = input_dev;
-
- ts->regmap = devm_regmap_init_spi(spi, &tsc2005_regmap_config);
- if (IS_ERR(ts->regmap))
- return PTR_ERR(ts->regmap);
-
- ts->x_plate_ohm = x_plate_ohm;
- ts->esd_timeout = esd_timeout;
-
- ts->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset",
- GPIOD_OUT_HIGH);
- if (IS_ERR(ts->reset_gpio)) {
- error = PTR_ERR(ts->reset_gpio);
- dev_err(&spi->dev, "error acquiring reset gpio: %d\n", error);
- return error;
- }
-
- ts->vio = devm_regulator_get_optional(&spi->dev, "vio");
- if (IS_ERR(ts->vio)) {
- error = PTR_ERR(ts->vio);
- dev_err(&spi->dev, "vio regulator missing (%d)", error);
- return error;
- }
-
- if (!ts->reset_gpio && pdata)
- ts->set_reset = pdata->set_reset;
-
- mutex_init(&ts->mutex);
-
- spin_lock_init(&ts->lock);
- setup_timer(&ts->penup_timer, tsc2005_penup_timer, (unsigned long)ts);
-
- INIT_DELAYED_WORK(&ts->esd_work, tsc2005_esd_work);
-
- snprintf(ts->phys, sizeof(ts->phys),
- "%s/input-ts", dev_name(&spi->dev));
-
- input_dev->name = "TSC2005 touchscreen";
- input_dev->phys = ts->phys;
- input_dev->id.bustype = BUS_SPI;
- input_dev->dev.parent = &spi->dev;
- input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
- input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
-
- input_set_abs_params(input_dev, ABS_X, 0, max_x, fudge_x, 0);
- input_set_abs_params(input_dev, ABS_Y, 0, max_y, fudge_y, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0);
-
- if (np)
- touchscreen_parse_properties(input_dev, false);
-
- input_dev->open = tsc2005_open;
- input_dev->close = tsc2005_close;
-
- input_set_drvdata(input_dev, ts);
-
- /* Ensure the touchscreen is off */
- tsc2005_stop_scan(ts);
-
- error = devm_request_threaded_irq(&spi->dev, spi->irq, NULL,
- tsc2005_irq_thread,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
- "tsc2005", ts);
- if (error) {
- dev_err(&spi->dev, "Failed to request irq, err: %d\n", error);
- return error;
- }
-
- /* enable regulator for DT */
- if (ts->vio) {
- error = regulator_enable(ts->vio);
- if (error)
- return error;
- }
-
- dev_set_drvdata(&spi->dev, ts);
- error = sysfs_create_group(&spi->dev.kobj, &tsc2005_attr_group);
- if (error) {
- dev_err(&spi->dev,
- "Failed to create sysfs attributes, err: %d\n", error);
- goto disable_regulator;
- }
-
- error = input_register_device(ts->idev);
- if (error) {
- dev_err(&spi->dev,
- "Failed to register input device, err: %d\n", error);
- goto err_remove_sysfs;
- }
-
- irq_set_irq_wake(spi->irq, 1);
- return 0;
-
-err_remove_sysfs:
- sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group);
-disable_regulator:
- if (ts->vio)
- regulator_disable(ts->vio);
- return error;
+ return tsc200x_probe(&spi->dev, spi->irq, BUS_SPI,
+ devm_regmap_init_spi(spi, &tsc200x_regmap_config),
+ tsc2005_cmd);
}
static int tsc2005_remove(struct spi_device *spi)
{
- struct tsc2005 *ts = dev_get_drvdata(&spi->dev);
-
- sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group);
-
- if (ts->vio)
- regulator_disable(ts->vio);
-
- return 0;
-}
-
-static int __maybe_unused tsc2005_suspend(struct device *dev)
-{
- struct tsc2005 *ts = dev_get_drvdata(dev);
-
- mutex_lock(&ts->mutex);
-
- if (!ts->suspended && ts->opened)
- __tsc2005_disable(ts);
-
- ts->suspended = true;
-
- mutex_unlock(&ts->mutex);
-
- return 0;
-}
-
-static int __maybe_unused tsc2005_resume(struct device *dev)
-{
- struct tsc2005 *ts = dev_get_drvdata(dev);
-
- mutex_lock(&ts->mutex);
-
- if (ts->suspended && ts->opened)
- __tsc2005_enable(ts);
-
- ts->suspended = false;
-
- mutex_unlock(&ts->mutex);
-
- return 0;
+ return tsc200x_remove(&spi->dev);
}
-static SIMPLE_DEV_PM_OPS(tsc2005_pm_ops, tsc2005_suspend, tsc2005_resume);
-
static struct spi_driver tsc2005_driver = {
.driver = {
.name = "tsc2005",
- .owner = THIS_MODULE,
- .pm = &tsc2005_pm_ops,
+ .pm = &tsc200x_pm_ops,
},
.probe = tsc2005_probe,
.remove = tsc2005_remove,
};
-
module_spi_driver(tsc2005_driver);
-MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>");
+MODULE_AUTHOR("Michael Welling <mwelling@ieee.org>");
MODULE_DESCRIPTION("TSC2005 Touchscreen Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:tsc2005");
#include <linux/skbuff.h>
#include <linux/of_gpio.h>
#include <linux/ieee802154.h>
+#include <linux/debugfs.h>
#include <net/mac802154.h>
#include <net/cfg802154.h>
u8 from_state;
u8 to_state;
- bool irq_enable;
+ bool free;
+};
+
+struct at86rf230_trac {
+ u64 success;
+ u64 success_data_pending;
+ u64 success_wait_for_ack;
+ u64 channel_access_failure;
+ u64 no_ack;
+ u64 invalid;
};
struct at86rf230_local {
struct completion state_complete;
struct at86rf230_state_change state;
- struct at86rf230_state_change irq;
-
unsigned long cal_timeout;
bool is_tx;
bool is_tx_from_off;
u8 tx_retry;
struct sk_buff *tx_skb;
struct at86rf230_state_change tx;
+
+ struct at86rf230_trac trac;
};
#define AT86RF2XX_NUMREGS 0x3F
static void
at86rf230_async_state_change(struct at86rf230_local *lp,
struct at86rf230_state_change *ctx,
- const u8 state, void (*complete)(void *context),
- const bool irq_enable);
+ const u8 state, void (*complete)(void *context));
static inline void
at86rf230_sleep(struct at86rf230_local *lp)
struct at86rf230_local *lp = ctx->lp;
lp->is_tx = 0;
- at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL, false);
+ at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL);
ieee802154_wake_queue(lp->hw);
+ if (ctx->free)
+ kfree(ctx);
}
static inline void
dev_err(&lp->spi->dev, "spi_async error %d\n", rc);
at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF,
- at86rf230_async_error_recover, false);
+ at86rf230_async_error_recover);
}
/* Generic function to get some register value in async mode */
static void
-at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg,
+at86rf230_async_read_reg(struct at86rf230_local *lp, u8 reg,
struct at86rf230_state_change *ctx,
- void (*complete)(void *context),
- const bool irq_enable)
+ void (*complete)(void *context))
{
int rc;
tx_buf[0] = (reg & CMD_REG_MASK) | CMD_REG;
ctx->msg.complete = complete;
- ctx->irq_enable = irq_enable;
rc = spi_async(lp->spi, &ctx->msg);
- if (rc) {
- if (irq_enable)
- enable_irq(ctx->irq);
-
+ if (rc)
at86rf230_async_error(lp, ctx, rc);
- }
}
-static inline u8 at86rf230_state_to_force(u8 state)
+static void
+at86rf230_async_write_reg(struct at86rf230_local *lp, u8 reg, u8 val,
+ struct at86rf230_state_change *ctx,
+ void (*complete)(void *context))
{
- if (state == STATE_TX_ON)
- return STATE_FORCE_TX_ON;
- else
- return STATE_FORCE_TRX_OFF;
+ int rc;
+
+ ctx->buf[0] = (reg & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
+ ctx->buf[1] = val;
+ ctx->msg.complete = complete;
+ rc = spi_async(lp->spi, &ctx->msg);
+ if (rc)
+ at86rf230_async_error(lp, ctx, rc);
}
static void
u8 state = ctx->to_state;
if (lp->tx_retry >= AT86RF2XX_MAX_TX_RETRIES)
- state = at86rf230_state_to_force(state);
+ state = STATE_FORCE_TRX_OFF;
lp->tx_retry++;
at86rf230_async_state_change(lp, ctx, state,
- ctx->complete,
- ctx->irq_enable);
+ ctx->complete);
return;
}
}
struct at86rf230_local *lp = ctx->lp;
at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
- at86rf230_async_state_assert,
- ctx->irq_enable);
+ at86rf230_async_state_assert);
return HRTIMER_NORESTART;
}
struct at86rf230_local *lp = ctx->lp;
u8 *buf = ctx->buf;
const u8 trx_state = buf[1] & TRX_STATE_MASK;
- int rc;
/* Check for "possible" STATE_TRANSITION_IN_PROGRESS */
if (trx_state == STATE_TRANSITION_IN_PROGRESS) {
udelay(1);
at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
- at86rf230_async_state_change_start,
- ctx->irq_enable);
+ at86rf230_async_state_change_start);
return;
}
/* Going into the next step for a state change which do a timing
* relevant delay.
*/
- buf[0] = (RG_TRX_STATE & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
- buf[1] = ctx->to_state;
- ctx->msg.complete = at86rf230_async_state_delay;
- rc = spi_async(lp->spi, &ctx->msg);
- if (rc) {
- if (ctx->irq_enable)
- enable_irq(ctx->irq);
-
- at86rf230_async_error(lp, ctx, rc);
- }
+ at86rf230_async_write_reg(lp, RG_TRX_STATE, ctx->to_state, ctx,
+ at86rf230_async_state_delay);
}
static void
at86rf230_async_state_change(struct at86rf230_local *lp,
struct at86rf230_state_change *ctx,
- const u8 state, void (*complete)(void *context),
- const bool irq_enable)
+ const u8 state, void (*complete)(void *context))
{
/* Initialization for the state change context */
ctx->to_state = state;
ctx->complete = complete;
- ctx->irq_enable = irq_enable;
at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
- at86rf230_async_state_change_start,
- irq_enable);
+ at86rf230_async_state_change_start);
}
static void
unsigned long rc;
at86rf230_async_state_change(lp, &lp->state, state,
- at86rf230_sync_state_change_complete,
- false);
+ at86rf230_sync_state_change_complete);
rc = wait_for_completion_timeout(&lp->state_complete,
msecs_to_jiffies(100));
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
- enable_irq(ctx->irq);
-
ieee802154_xmit_complete(lp->hw, lp->tx_skb, false);
+ kfree(ctx);
}
static void
struct at86rf230_local *lp = ctx->lp;
at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON,
- at86rf230_tx_complete, true);
+ at86rf230_tx_complete);
}
static void
{
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
- const u8 *buf = ctx->buf;
- const u8 trac = (buf[1] & 0xe0) >> 5;
- /* If trac status is different than zero we need to do a state change
- * to STATE_FORCE_TRX_OFF then STATE_RX_AACK_ON to recover the
- * transceiver.
- */
- if (trac)
- at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF,
- at86rf230_tx_on, true);
- else
- at86rf230_tx_on(context);
-}
+ if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS)) {
+ u8 trac = TRAC_MASK(ctx->buf[1]);
-static void
-at86rf230_tx_trac_status(void *context)
-{
- struct at86rf230_state_change *ctx = context;
- struct at86rf230_local *lp = ctx->lp;
+ switch (trac) {
+ case TRAC_SUCCESS:
+ lp->trac.success++;
+ break;
+ case TRAC_SUCCESS_DATA_PENDING:
+ lp->trac.success_data_pending++;
+ break;
+ case TRAC_CHANNEL_ACCESS_FAILURE:
+ lp->trac.channel_access_failure++;
+ break;
+ case TRAC_NO_ACK:
+ lp->trac.no_ack++;
+ break;
+ case TRAC_INVALID:
+ lp->trac.invalid++;
+ break;
+ default:
+ WARN_ONCE(1, "received tx trac status %d\n", trac);
+ break;
+ }
+ }
- at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx,
- at86rf230_tx_trac_check, true);
+ at86rf230_async_state_change(lp, ctx, STATE_TX_ON, at86rf230_tx_on);
}
static void
{
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
- u8 rx_local_buf[AT86RF2XX_MAX_BUF];
const u8 *buf = ctx->buf;
struct sk_buff *skb;
u8 len, lqi;
}
lqi = buf[2 + len];
- memcpy(rx_local_buf, buf + 2, len);
- ctx->trx.len = 2;
- enable_irq(ctx->irq);
-
skb = dev_alloc_skb(IEEE802154_MTU);
if (!skb) {
dev_vdbg(&lp->spi->dev, "failed to allocate sk_buff\n");
+ kfree(ctx);
return;
}
- memcpy(skb_put(skb, len), rx_local_buf, len);
+ memcpy(skb_put(skb, len), buf + 2, len);
ieee802154_rx_irqsafe(lp->hw, skb, lqi);
+ kfree(ctx);
}
static void
-at86rf230_rx_read_frame(void *context)
+at86rf230_rx_trac_check(void *context)
{
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
u8 *buf = ctx->buf;
int rc;
+ if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS)) {
+ u8 trac = TRAC_MASK(buf[1]);
+
+ switch (trac) {
+ case TRAC_SUCCESS:
+ lp->trac.success++;
+ break;
+ case TRAC_SUCCESS_WAIT_FOR_ACK:
+ lp->trac.success_wait_for_ack++;
+ break;
+ case TRAC_INVALID:
+ lp->trac.invalid++;
+ break;
+ default:
+ WARN_ONCE(1, "received rx trac status %d\n", trac);
+ break;
+ }
+ }
+
buf[0] = CMD_FB;
ctx->trx.len = AT86RF2XX_MAX_BUF;
ctx->msg.complete = at86rf230_rx_read_frame_complete;
rc = spi_async(lp->spi, &ctx->msg);
if (rc) {
ctx->trx.len = 2;
- enable_irq(ctx->irq);
at86rf230_async_error(lp, ctx, rc);
}
}
static void
-at86rf230_rx_trac_check(void *context)
+at86rf230_irq_trx_end(void *context)
{
- /* Possible check on trac status here. This could be useful to make
- * some stats why receive is failed. Not used at the moment, but it's
- * maybe timing relevant. Datasheet doesn't say anything about this.
- * The programming guide say do it so.
- */
-
- at86rf230_rx_read_frame(context);
-}
+ struct at86rf230_state_change *ctx = context;
+ struct at86rf230_local *lp = ctx->lp;
-static void
-at86rf230_irq_trx_end(struct at86rf230_local *lp)
-{
if (lp->is_tx) {
lp->is_tx = 0;
- at86rf230_async_state_change(lp, &lp->irq,
- STATE_FORCE_TX_ON,
- at86rf230_tx_trac_status,
- true);
+ at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx,
+ at86rf230_tx_trac_check);
} else {
- at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq,
- at86rf230_rx_trac_check, true);
+ at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx,
+ at86rf230_rx_trac_check);
}
}
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
const u8 *buf = ctx->buf;
- const u8 irq = buf[1];
+ u8 irq = buf[1];
+
+ enable_irq(lp->spi->irq);
if (irq & IRQ_TRX_END) {
- at86rf230_irq_trx_end(lp);
+ at86rf230_irq_trx_end(ctx);
} else {
- enable_irq(ctx->irq);
dev_err(&lp->spi->dev, "not supported irq %02x received\n",
irq);
+ kfree(ctx);
}
}
+static void
+at86rf230_setup_spi_messages(struct at86rf230_local *lp,
+ struct at86rf230_state_change *state)
+{
+ state->lp = lp;
+ state->irq = lp->spi->irq;
+ spi_message_init(&state->msg);
+ state->msg.context = state;
+ state->trx.len = 2;
+ state->trx.tx_buf = state->buf;
+ state->trx.rx_buf = state->buf;
+ spi_message_add_tail(&state->trx, &state->msg);
+ hrtimer_init(&state->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ state->timer.function = at86rf230_async_state_timer;
+}
+
static irqreturn_t at86rf230_isr(int irq, void *data)
{
struct at86rf230_local *lp = data;
- struct at86rf230_state_change *ctx = &lp->irq;
- u8 *buf = ctx->buf;
+ struct at86rf230_state_change *ctx;
int rc;
disable_irq_nosync(irq);
- buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG;
+ ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC);
+ if (!ctx) {
+ enable_irq(irq);
+ return IRQ_NONE;
+ }
+
+ at86rf230_setup_spi_messages(lp, ctx);
+ /* tell on error handling to free ctx */
+ ctx->free = true;
+
+ ctx->buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG;
ctx->msg.complete = at86rf230_irq_status;
rc = spi_async(lp->spi, &ctx->msg);
if (rc) {
- enable_irq(irq);
at86rf230_async_error(lp, ctx, rc);
+ enable_irq(irq);
return IRQ_NONE;
}
{
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
- u8 *buf = ctx->buf;
- int rc;
ctx->trx.len = 2;
- if (gpio_is_valid(lp->slp_tr)) {
+ if (gpio_is_valid(lp->slp_tr))
at86rf230_slp_tr_rising_edge(lp);
- } else {
- buf[0] = (RG_TRX_STATE & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
- buf[1] = STATE_BUSY_TX;
- ctx->msg.complete = NULL;
- rc = spi_async(lp->spi, &ctx->msg);
- if (rc)
- at86rf230_async_error(lp, ctx, rc);
- }
+ else
+ at86rf230_async_write_reg(lp, RG_TRX_STATE, STATE_BUSY_TX, ctx,
+ NULL);
}
static void
struct at86rf230_local *lp = ctx->lp;
at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
- at86rf230_write_frame, false);
+ at86rf230_write_frame);
}
static void
if (lp->is_tx_from_off) {
lp->is_tx_from_off = false;
at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
- at86rf230_write_frame,
- false);
+ at86rf230_write_frame);
} else {
at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
- at86rf230_xmit_tx_on,
- false);
+ at86rf230_xmit_tx_on);
}
}
if (time_is_before_jiffies(lp->cal_timeout)) {
lp->is_tx_from_off = true;
at86rf230_async_state_change(lp, ctx, STATE_TRX_OFF,
- at86rf230_xmit_start, false);
+ at86rf230_xmit_start);
} else {
at86rf230_xmit_start(ctx);
}
{
struct at86rf230_local *lp = hw->priv;
+ /* reset trac stats on start */
+ if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS))
+ memset(&lp->trac, 0, sizeof(struct at86rf230_trac));
+
at86rf230_awake(lp);
enable_irq(lp->spi->irq);
return rc;
irq_type = irq_get_trigger_type(lp->spi->irq);
- if (irq_type == IRQ_TYPE_EDGE_RISING ||
- irq_type == IRQ_TYPE_EDGE_FALLING)
- dev_warn(&lp->spi->dev,
- "Using edge triggered irq's are not recommended!\n");
if (irq_type == IRQ_TYPE_EDGE_FALLING ||
irq_type == IRQ_TYPE_LEVEL_LOW)
irq_pol = IRQ_ACTIVE_LOW;
return rc;
}
-static void
-at86rf230_setup_spi_messages(struct at86rf230_local *lp)
+#ifdef CONFIG_IEEE802154_AT86RF230_DEBUGFS
+static struct dentry *at86rf230_debugfs_root;
+
+static int at86rf230_stats_show(struct seq_file *file, void *offset)
+{
+ struct at86rf230_local *lp = file->private;
+
+ seq_printf(file, "SUCCESS:\t\t%8llu\n", lp->trac.success);
+ seq_printf(file, "SUCCESS_DATA_PENDING:\t%8llu\n",
+ lp->trac.success_data_pending);
+ seq_printf(file, "SUCCESS_WAIT_FOR_ACK:\t%8llu\n",
+ lp->trac.success_wait_for_ack);
+ seq_printf(file, "CHANNEL_ACCESS_FAILURE:\t%8llu\n",
+ lp->trac.channel_access_failure);
+ seq_printf(file, "NO_ACK:\t\t\t%8llu\n", lp->trac.no_ack);
+ seq_printf(file, "INVALID:\t\t%8llu\n", lp->trac.invalid);
+ return 0;
+}
+
+static int at86rf230_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, at86rf230_stats_show, inode->i_private);
+}
+
+static const struct file_operations at86rf230_stats_fops = {
+ .open = at86rf230_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int at86rf230_debugfs_init(struct at86rf230_local *lp)
+{
+ char debugfs_dir_name[DNAME_INLINE_LEN + 1] = "at86rf230-";
+ struct dentry *stats;
+
+ strncat(debugfs_dir_name, dev_name(&lp->spi->dev), DNAME_INLINE_LEN);
+
+ at86rf230_debugfs_root = debugfs_create_dir(debugfs_dir_name, NULL);
+ if (!at86rf230_debugfs_root)
+ return -ENOMEM;
+
+ stats = debugfs_create_file("trac_stats", S_IRUGO,
+ at86rf230_debugfs_root, lp,
+ &at86rf230_stats_fops);
+ if (!stats)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void at86rf230_debugfs_remove(void)
{
- lp->state.lp = lp;
- lp->state.irq = lp->spi->irq;
- spi_message_init(&lp->state.msg);
- lp->state.msg.context = &lp->state;
- lp->state.trx.len = 2;
- lp->state.trx.tx_buf = lp->state.buf;
- lp->state.trx.rx_buf = lp->state.buf;
- spi_message_add_tail(&lp->state.trx, &lp->state.msg);
- hrtimer_init(&lp->state.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- lp->state.timer.function = at86rf230_async_state_timer;
-
- lp->irq.lp = lp;
- lp->irq.irq = lp->spi->irq;
- spi_message_init(&lp->irq.msg);
- lp->irq.msg.context = &lp->irq;
- lp->irq.trx.len = 2;
- lp->irq.trx.tx_buf = lp->irq.buf;
- lp->irq.trx.rx_buf = lp->irq.buf;
- spi_message_add_tail(&lp->irq.trx, &lp->irq.msg);
- hrtimer_init(&lp->irq.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- lp->irq.timer.function = at86rf230_async_state_timer;
-
- lp->tx.lp = lp;
- lp->tx.irq = lp->spi->irq;
- spi_message_init(&lp->tx.msg);
- lp->tx.msg.context = &lp->tx;
- lp->tx.trx.len = 2;
- lp->tx.trx.tx_buf = lp->tx.buf;
- lp->tx.trx.rx_buf = lp->tx.buf;
- spi_message_add_tail(&lp->tx.trx, &lp->tx.msg);
- hrtimer_init(&lp->tx.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- lp->tx.timer.function = at86rf230_async_state_timer;
+ debugfs_remove_recursive(at86rf230_debugfs_root);
}
+#else
+static int at86rf230_debugfs_init(struct at86rf230_local *lp) { return 0; }
+static void at86rf230_debugfs_remove(void) { }
+#endif
static int at86rf230_probe(struct spi_device *spi)
{
goto free_dev;
}
- at86rf230_setup_spi_messages(lp);
+ at86rf230_setup_spi_messages(lp, &lp->state);
+ at86rf230_setup_spi_messages(lp, &lp->tx);
rc = at86rf230_detect_device(lp);
if (rc < 0)
/* going into sleep by default */
at86rf230_sleep(lp);
- rc = ieee802154_register_hw(lp->hw);
+ rc = at86rf230_debugfs_init(lp);
if (rc)
goto free_dev;
+ rc = ieee802154_register_hw(lp->hw);
+ if (rc)
+ goto free_debugfs;
+
return rc;
+free_debugfs:
+ at86rf230_debugfs_remove();
free_dev:
ieee802154_free_hw(lp->hw);
at86rf230_write_subreg(lp, SR_IRQ_MASK, 0);
ieee802154_unregister_hw(lp->hw);
ieee802154_free_hw(lp->hw);
+ at86rf230_debugfs_remove();
dev_dbg(&spi->dev, "unregistered at86rf230\n");
return 0;
.driver = {
.of_match_table = of_match_ptr(at86rf230_of_match),
.name = "at86rf230",
- .owner = THIS_MODULE,
},
.probe = at86rf230_probe,
.remove = at86rf230_remove,
#include <linux/spi/spi.h>
#include <linux/interrupt.h>
#include <linux/module.h>
+#include <linux/regmap.h>
#include <linux/ieee802154.h>
+#include <linux/irq.h>
#include <net/cfg802154.h>
#include <net/mac802154.h>
/* MRF24J40 Short Address Registers */
-#define REG_RXMCR 0x00 /* Receive MAC control */
-#define REG_PANIDL 0x01 /* PAN ID (low) */
-#define REG_PANIDH 0x02 /* PAN ID (high) */
-#define REG_SADRL 0x03 /* Short address (low) */
-#define REG_SADRH 0x04 /* Short address (high) */
-#define REG_EADR0 0x05 /* Long address (low) (high is EADR7) */
-#define REG_TXMCR 0x11 /* Transmit MAC control */
-#define REG_PACON0 0x16 /* Power Amplifier Control */
-#define REG_PACON1 0x17 /* Power Amplifier Control */
-#define REG_PACON2 0x18 /* Power Amplifier Control */
-#define REG_TXNCON 0x1B /* Transmit Normal FIFO Control */
-#define REG_TXSTAT 0x24 /* TX MAC Status Register */
-#define REG_SOFTRST 0x2A /* Soft Reset */
-#define REG_TXSTBL 0x2E /* TX Stabilization */
-#define REG_INTSTAT 0x31 /* Interrupt Status */
-#define REG_INTCON 0x32 /* Interrupt Control */
-#define REG_GPIO 0x33 /* GPIO */
-#define REG_TRISGPIO 0x34 /* GPIO direction */
-#define REG_RFCTL 0x36 /* RF Control Mode Register */
-#define REG_BBREG1 0x39 /* Baseband Registers */
-#define REG_BBREG2 0x3A /* */
-#define REG_BBREG6 0x3E /* */
-#define REG_CCAEDTH 0x3F /* Energy Detection Threshold */
+#define REG_RXMCR 0x00 /* Receive MAC control */
+#define BIT_PROMI BIT(0)
+#define BIT_ERRPKT BIT(1)
+#define BIT_NOACKRSP BIT(5)
+#define BIT_PANCOORD BIT(3)
+
+#define REG_PANIDL 0x01 /* PAN ID (low) */
+#define REG_PANIDH 0x02 /* PAN ID (high) */
+#define REG_SADRL 0x03 /* Short address (low) */
+#define REG_SADRH 0x04 /* Short address (high) */
+#define REG_EADR0 0x05 /* Long address (low) (high is EADR7) */
+#define REG_EADR1 0x06
+#define REG_EADR2 0x07
+#define REG_EADR3 0x08
+#define REG_EADR4 0x09
+#define REG_EADR5 0x0A
+#define REG_EADR6 0x0B
+#define REG_EADR7 0x0C
+#define REG_RXFLUSH 0x0D
+#define REG_ORDER 0x10
+#define REG_TXMCR 0x11 /* Transmit MAC control */
+#define TXMCR_MIN_BE_SHIFT 3
+#define TXMCR_MIN_BE_MASK 0x18
+#define TXMCR_CSMA_RETRIES_SHIFT 0
+#define TXMCR_CSMA_RETRIES_MASK 0x07
+
+#define REG_ACKTMOUT 0x12
+#define REG_ESLOTG1 0x13
+#define REG_SYMTICKL 0x14
+#define REG_SYMTICKH 0x15
+#define REG_PACON0 0x16 /* Power Amplifier Control */
+#define REG_PACON1 0x17 /* Power Amplifier Control */
+#define REG_PACON2 0x18 /* Power Amplifier Control */
+#define REG_TXBCON0 0x1A
+#define REG_TXNCON 0x1B /* Transmit Normal FIFO Control */
+#define BIT_TXNTRIG BIT(0)
+#define BIT_TXNACKREQ BIT(2)
+
+#define REG_TXG1CON 0x1C
+#define REG_TXG2CON 0x1D
+#define REG_ESLOTG23 0x1E
+#define REG_ESLOTG45 0x1F
+#define REG_ESLOTG67 0x20
+#define REG_TXPEND 0x21
+#define REG_WAKECON 0x22
+#define REG_FROMOFFSET 0x23
+#define REG_TXSTAT 0x24 /* TX MAC Status Register */
+#define REG_TXBCON1 0x25
+#define REG_GATECLK 0x26
+#define REG_TXTIME 0x27
+#define REG_HSYMTMRL 0x28
+#define REG_HSYMTMRH 0x29
+#define REG_SOFTRST 0x2A /* Soft Reset */
+#define REG_SECCON0 0x2C
+#define REG_SECCON1 0x2D
+#define REG_TXSTBL 0x2E /* TX Stabilization */
+#define REG_RXSR 0x30
+#define REG_INTSTAT 0x31 /* Interrupt Status */
+#define BIT_TXNIF BIT(0)
+#define BIT_RXIF BIT(3)
+
+#define REG_INTCON 0x32 /* Interrupt Control */
+#define BIT_TXNIE BIT(0)
+#define BIT_RXIE BIT(3)
+
+#define REG_GPIO 0x33 /* GPIO */
+#define REG_TRISGPIO 0x34 /* GPIO direction */
+#define REG_SLPACK 0x35
+#define REG_RFCTL 0x36 /* RF Control Mode Register */
+#define BIT_RFRST BIT(2)
+
+#define REG_SECCR2 0x37
+#define REG_BBREG0 0x38
+#define REG_BBREG1 0x39 /* Baseband Registers */
+#define BIT_RXDECINV BIT(2)
+
+#define REG_BBREG2 0x3A /* */
+#define BBREG2_CCA_MODE_SHIFT 6
+#define BBREG2_CCA_MODE_MASK 0xc0
+
+#define REG_BBREG3 0x3B
+#define REG_BBREG4 0x3C
+#define REG_BBREG6 0x3E /* */
+#define REG_CCAEDTH 0x3F /* Energy Detection Threshold */
/* MRF24J40 Long Address Registers */
-#define REG_RFCON0 0x200 /* RF Control Registers */
-#define REG_RFCON1 0x201
-#define REG_RFCON2 0x202
-#define REG_RFCON3 0x203
-#define REG_RFCON5 0x205
-#define REG_RFCON6 0x206
-#define REG_RFCON7 0x207
-#define REG_RFCON8 0x208
-#define REG_RSSI 0x210
-#define REG_SLPCON0 0x211 /* Sleep Clock Control Registers */
-#define REG_SLPCON1 0x220
-#define REG_WAKETIMEL 0x222 /* Wake-up Time Match Value Low */
-#define REG_WAKETIMEH 0x223 /* Wake-up Time Match Value High */
-#define REG_TESTMODE 0x22F /* Test mode */
-#define REG_RX_FIFO 0x300 /* Receive FIFO */
+#define REG_RFCON0 0x200 /* RF Control Registers */
+#define RFCON0_CH_SHIFT 4
+#define RFCON0_CH_MASK 0xf0
+#define RFOPT_RECOMMEND 3
+
+#define REG_RFCON1 0x201
+#define REG_RFCON2 0x202
+#define REG_RFCON3 0x203
+
+#define TXPWRL_MASK 0xc0
+#define TXPWRL_SHIFT 6
+#define TXPWRL_30 0x3
+#define TXPWRL_20 0x2
+#define TXPWRL_10 0x1
+#define TXPWRL_0 0x0
+
+#define TXPWRS_MASK 0x38
+#define TXPWRS_SHIFT 3
+#define TXPWRS_6_3 0x7
+#define TXPWRS_4_9 0x6
+#define TXPWRS_3_7 0x5
+#define TXPWRS_2_8 0x4
+#define TXPWRS_1_9 0x3
+#define TXPWRS_1_2 0x2
+#define TXPWRS_0_5 0x1
+#define TXPWRS_0 0x0
+
+#define REG_RFCON5 0x205
+#define REG_RFCON6 0x206
+#define REG_RFCON7 0x207
+#define REG_RFCON8 0x208
+#define REG_SLPCAL0 0x209
+#define REG_SLPCAL1 0x20A
+#define REG_SLPCAL2 0x20B
+#define REG_RFSTATE 0x20F
+#define REG_RSSI 0x210
+#define REG_SLPCON0 0x211 /* Sleep Clock Control Registers */
+#define BIT_INTEDGE BIT(1)
+
+#define REG_SLPCON1 0x220
+#define REG_WAKETIMEL 0x222 /* Wake-up Time Match Value Low */
+#define REG_WAKETIMEH 0x223 /* Wake-up Time Match Value High */
+#define REG_REMCNTL 0x224
+#define REG_REMCNTH 0x225
+#define REG_MAINCNT0 0x226
+#define REG_MAINCNT1 0x227
+#define REG_MAINCNT2 0x228
+#define REG_MAINCNT3 0x229
+#define REG_TESTMODE 0x22F /* Test mode */
+#define REG_ASSOEAR0 0x230
+#define REG_ASSOEAR1 0x231
+#define REG_ASSOEAR2 0x232
+#define REG_ASSOEAR3 0x233
+#define REG_ASSOEAR4 0x234
+#define REG_ASSOEAR5 0x235
+#define REG_ASSOEAR6 0x236
+#define REG_ASSOEAR7 0x237
+#define REG_ASSOSAR0 0x238
+#define REG_ASSOSAR1 0x239
+#define REG_UNONCE0 0x240
+#define REG_UNONCE1 0x241
+#define REG_UNONCE2 0x242
+#define REG_UNONCE3 0x243
+#define REG_UNONCE4 0x244
+#define REG_UNONCE5 0x245
+#define REG_UNONCE6 0x246
+#define REG_UNONCE7 0x247
+#define REG_UNONCE8 0x248
+#define REG_UNONCE9 0x249
+#define REG_UNONCE10 0x24A
+#define REG_UNONCE11 0x24B
+#define REG_UNONCE12 0x24C
+#define REG_RX_FIFO 0x300 /* Receive FIFO */
/* Device configuration: Only channels 11-26 on page 0 are supported. */
#define MRF24J40_CHAN_MIN 11
struct spi_device *spi;
struct ieee802154_hw *hw;
- struct mutex buffer_mutex; /* only used to protect buf */
- struct completion tx_complete;
- u8 *buf; /* 3 bytes. Used for SPI single-register transfers. */
+ struct regmap *regmap_short;
+ struct regmap *regmap_long;
+
+ /* for writing txfifo */
+ struct spi_message tx_msg;
+ u8 tx_hdr_buf[2];
+ struct spi_transfer tx_hdr_trx;
+ u8 tx_len_buf[2];
+ struct spi_transfer tx_len_trx;
+ struct spi_transfer tx_buf_trx;
+ struct sk_buff *tx_skb;
+
+ /* post transmit message to send frame out */
+ struct spi_message tx_post_msg;
+ u8 tx_post_buf[2];
+ struct spi_transfer tx_post_trx;
+
+ /* for protect/unprotect/read length rxfifo */
+ struct spi_message rx_msg;
+ u8 rx_buf[3];
+ struct spi_transfer rx_trx;
+
+ /* receive handling */
+ struct spi_message rx_buf_msg;
+ u8 rx_addr_buf[2];
+ struct spi_transfer rx_addr_trx;
+ u8 rx_lqi_buf[2];
+ struct spi_transfer rx_lqi_trx;
+ u8 rx_fifo_buf[RX_FIFO_SIZE];
+ struct spi_transfer rx_fifo_buf_trx;
+
+ /* isr handling for reading intstat */
+ struct spi_message irq_msg;
+ u8 irq_buf[2];
+ struct spi_transfer irq_trx;
};
+/* regmap information for short address register access */
+#define MRF24J40_SHORT_WRITE 0x01
+#define MRF24J40_SHORT_READ 0x00
+#define MRF24J40_SHORT_NUMREGS 0x3F
+
+/* regmap information for long address register access */
+#define MRF24J40_LONG_ACCESS 0x80
+#define MRF24J40_LONG_NUMREGS 0x38F
+
/* Read/Write SPI Commands for Short and Long Address registers. */
#define MRF24J40_READSHORT(reg) ((reg) << 1)
#define MRF24J40_WRITESHORT(reg) ((reg) << 1 | 1)
#define printdev(X) (&X->spi->dev)
-static int write_short_reg(struct mrf24j40 *devrec, u8 reg, u8 value)
+static bool
+mrf24j40_short_reg_writeable(struct device *dev, unsigned int reg)
{
- int ret;
- struct spi_message msg;
- struct spi_transfer xfer = {
- .len = 2,
- .tx_buf = devrec->buf,
- .rx_buf = devrec->buf,
- };
+ switch (reg) {
+ case REG_RXMCR:
+ case REG_PANIDL:
+ case REG_PANIDH:
+ case REG_SADRL:
+ case REG_SADRH:
+ case REG_EADR0:
+ case REG_EADR1:
+ case REG_EADR2:
+ case REG_EADR3:
+ case REG_EADR4:
+ case REG_EADR5:
+ case REG_EADR6:
+ case REG_EADR7:
+ case REG_RXFLUSH:
+ case REG_ORDER:
+ case REG_TXMCR:
+ case REG_ACKTMOUT:
+ case REG_ESLOTG1:
+ case REG_SYMTICKL:
+ case REG_SYMTICKH:
+ case REG_PACON0:
+ case REG_PACON1:
+ case REG_PACON2:
+ case REG_TXBCON0:
+ case REG_TXNCON:
+ case REG_TXG1CON:
+ case REG_TXG2CON:
+ case REG_ESLOTG23:
+ case REG_ESLOTG45:
+ case REG_ESLOTG67:
+ case REG_TXPEND:
+ case REG_WAKECON:
+ case REG_FROMOFFSET:
+ case REG_TXBCON1:
+ case REG_GATECLK:
+ case REG_TXTIME:
+ case REG_HSYMTMRL:
+ case REG_HSYMTMRH:
+ case REG_SOFTRST:
+ case REG_SECCON0:
+ case REG_SECCON1:
+ case REG_TXSTBL:
+ case REG_RXSR:
+ case REG_INTCON:
+ case REG_TRISGPIO:
+ case REG_GPIO:
+ case REG_RFCTL:
+ case REG_SLPACK:
+ case REG_BBREG0:
+ case REG_BBREG1:
+ case REG_BBREG2:
+ case REG_BBREG3:
+ case REG_BBREG4:
+ case REG_BBREG6:
+ case REG_CCAEDTH:
+ return true;
+ default:
+ return false;
+ }
+}
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
+static bool
+mrf24j40_short_reg_readable(struct device *dev, unsigned int reg)
+{
+ bool rc;
+
+ /* all writeable are also readable */
+ rc = mrf24j40_short_reg_writeable(dev, reg);
+ if (rc)
+ return rc;
+
+ /* readonly regs */
+ switch (reg) {
+ case REG_TXSTAT:
+ case REG_INTSTAT:
+ return true;
+ default:
+ return false;
+ }
+}
- mutex_lock(&devrec->buffer_mutex);
- devrec->buf[0] = MRF24J40_WRITESHORT(reg);
- devrec->buf[1] = value;
+static bool
+mrf24j40_short_reg_volatile(struct device *dev, unsigned int reg)
+{
+ /* can be changed during runtime */
+ switch (reg) {
+ case REG_TXSTAT:
+ case REG_INTSTAT:
+ case REG_RXFLUSH:
+ case REG_TXNCON:
+ case REG_SOFTRST:
+ case REG_RFCTL:
+ case REG_TXBCON0:
+ case REG_TXG1CON:
+ case REG_TXG2CON:
+ case REG_TXBCON1:
+ case REG_SECCON0:
+ case REG_RXSR:
+ case REG_SLPACK:
+ case REG_SECCR2:
+ case REG_BBREG6:
+ /* use them in spi_async and regmap so it's volatile */
+ case REG_BBREG1:
+ return true;
+ default:
+ return false;
+ }
+}
- ret = spi_sync(devrec->spi, &msg);
- if (ret)
- dev_err(printdev(devrec),
- "SPI write Failed for short register 0x%hhx\n", reg);
+static bool
+mrf24j40_short_reg_precious(struct device *dev, unsigned int reg)
+{
+ /* don't clear irq line on read */
+ switch (reg) {
+ case REG_INTSTAT:
+ return true;
+ default:
+ return false;
+ }
+}
- mutex_unlock(&devrec->buffer_mutex);
- return ret;
+static const struct regmap_config mrf24j40_short_regmap = {
+ .name = "mrf24j40_short",
+ .reg_bits = 7,
+ .val_bits = 8,
+ .pad_bits = 1,
+ .write_flag_mask = MRF24J40_SHORT_WRITE,
+ .read_flag_mask = MRF24J40_SHORT_READ,
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = MRF24J40_SHORT_NUMREGS,
+ .writeable_reg = mrf24j40_short_reg_writeable,
+ .readable_reg = mrf24j40_short_reg_readable,
+ .volatile_reg = mrf24j40_short_reg_volatile,
+ .precious_reg = mrf24j40_short_reg_precious,
+};
+
+static bool
+mrf24j40_long_reg_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_RFCON0:
+ case REG_RFCON1:
+ case REG_RFCON2:
+ case REG_RFCON3:
+ case REG_RFCON5:
+ case REG_RFCON6:
+ case REG_RFCON7:
+ case REG_RFCON8:
+ case REG_SLPCAL2:
+ case REG_SLPCON0:
+ case REG_SLPCON1:
+ case REG_WAKETIMEL:
+ case REG_WAKETIMEH:
+ case REG_REMCNTL:
+ case REG_REMCNTH:
+ case REG_MAINCNT0:
+ case REG_MAINCNT1:
+ case REG_MAINCNT2:
+ case REG_MAINCNT3:
+ case REG_TESTMODE:
+ case REG_ASSOEAR0:
+ case REG_ASSOEAR1:
+ case REG_ASSOEAR2:
+ case REG_ASSOEAR3:
+ case REG_ASSOEAR4:
+ case REG_ASSOEAR5:
+ case REG_ASSOEAR6:
+ case REG_ASSOEAR7:
+ case REG_ASSOSAR0:
+ case REG_ASSOSAR1:
+ case REG_UNONCE0:
+ case REG_UNONCE1:
+ case REG_UNONCE2:
+ case REG_UNONCE3:
+ case REG_UNONCE4:
+ case REG_UNONCE5:
+ case REG_UNONCE6:
+ case REG_UNONCE7:
+ case REG_UNONCE8:
+ case REG_UNONCE9:
+ case REG_UNONCE10:
+ case REG_UNONCE11:
+ case REG_UNONCE12:
+ return true;
+ default:
+ return false;
+ }
}
-static int read_short_reg(struct mrf24j40 *devrec, u8 reg, u8 *val)
+static bool
+mrf24j40_long_reg_readable(struct device *dev, unsigned int reg)
{
- int ret = -1;
- struct spi_message msg;
- struct spi_transfer xfer = {
- .len = 2,
- .tx_buf = devrec->buf,
- .rx_buf = devrec->buf,
- };
+ bool rc;
+
+ /* all writeable are also readable */
+ rc = mrf24j40_long_reg_writeable(dev, reg);
+ if (rc)
+ return rc;
+
+ /* readonly regs */
+ switch (reg) {
+ case REG_SLPCAL0:
+ case REG_SLPCAL1:
+ case REG_RFSTATE:
+ case REG_RSSI:
+ return true;
+ default:
+ return false;
+ }
+}
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
+static bool
+mrf24j40_long_reg_volatile(struct device *dev, unsigned int reg)
+{
+ /* can be changed during runtime */
+ switch (reg) {
+ case REG_SLPCAL0:
+ case REG_SLPCAL1:
+ case REG_SLPCAL2:
+ case REG_RFSTATE:
+ case REG_RSSI:
+ case REG_MAINCNT3:
+ return true;
+ default:
+ return false;
+ }
+}
- mutex_lock(&devrec->buffer_mutex);
- devrec->buf[0] = MRF24J40_READSHORT(reg);
- devrec->buf[1] = 0;
+static const struct regmap_config mrf24j40_long_regmap = {
+ .name = "mrf24j40_long",
+ .reg_bits = 11,
+ .val_bits = 8,
+ .pad_bits = 5,
+ .write_flag_mask = MRF24J40_LONG_ACCESS,
+ .read_flag_mask = MRF24J40_LONG_ACCESS,
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = MRF24J40_LONG_NUMREGS,
+ .writeable_reg = mrf24j40_long_reg_writeable,
+ .readable_reg = mrf24j40_long_reg_readable,
+ .volatile_reg = mrf24j40_long_reg_volatile,
+};
- ret = spi_sync(devrec->spi, &msg);
- if (ret)
- dev_err(printdev(devrec),
- "SPI read Failed for short register 0x%hhx\n", reg);
- else
- *val = devrec->buf[1];
+static int mrf24j40_long_regmap_write(void *context, const void *data,
+ size_t count)
+{
+ struct spi_device *spi = context;
+ u8 buf[3];
- mutex_unlock(&devrec->buffer_mutex);
- return ret;
+ if (count > 3)
+ return -EINVAL;
+
+ /* regmap supports read/write mask only in frist byte
+ * long write access need to set the 12th bit, so we
+ * make special handling for write.
+ */
+ memcpy(buf, data, count);
+ buf[1] |= (1 << 4);
+
+ return spi_write(spi, buf, count);
}
-static int read_long_reg(struct mrf24j40 *devrec, u16 reg, u8 *value)
+static int
+mrf24j40_long_regmap_read(void *context, const void *reg, size_t reg_size,
+ void *val, size_t val_size)
{
- int ret;
- u16 cmd;
- struct spi_message msg;
- struct spi_transfer xfer = {
- .len = 3,
- .tx_buf = devrec->buf,
- .rx_buf = devrec->buf,
- };
-
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
-
- cmd = MRF24J40_READLONG(reg);
- mutex_lock(&devrec->buffer_mutex);
- devrec->buf[0] = cmd >> 8 & 0xff;
- devrec->buf[1] = cmd & 0xff;
- devrec->buf[2] = 0;
-
- ret = spi_sync(devrec->spi, &msg);
- if (ret)
- dev_err(printdev(devrec),
- "SPI read Failed for long register 0x%hx\n", reg);
- else
- *value = devrec->buf[2];
+ struct spi_device *spi = context;
- mutex_unlock(&devrec->buffer_mutex);
- return ret;
+ return spi_write_then_read(spi, reg, reg_size, val, val_size);
}
-static int write_long_reg(struct mrf24j40 *devrec, u16 reg, u8 val)
+static const struct regmap_bus mrf24j40_long_regmap_bus = {
+ .write = mrf24j40_long_regmap_write,
+ .read = mrf24j40_long_regmap_read,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static void write_tx_buf_complete(void *context)
{
+ struct mrf24j40 *devrec = context;
+ __le16 fc = ieee802154_get_fc_from_skb(devrec->tx_skb);
+ u8 val = BIT_TXNTRIG;
int ret;
- u16 cmd;
- struct spi_message msg;
- struct spi_transfer xfer = {
- .len = 3,
- .tx_buf = devrec->buf,
- .rx_buf = devrec->buf,
- };
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
+ if (ieee802154_is_ackreq(fc))
+ val |= BIT_TXNACKREQ;
- cmd = MRF24J40_WRITELONG(reg);
- mutex_lock(&devrec->buffer_mutex);
- devrec->buf[0] = cmd >> 8 & 0xff;
- devrec->buf[1] = cmd & 0xff;
- devrec->buf[2] = val;
+ devrec->tx_post_msg.complete = NULL;
+ devrec->tx_post_buf[0] = MRF24J40_WRITESHORT(REG_TXNCON);
+ devrec->tx_post_buf[1] = val;
- ret = spi_sync(devrec->spi, &msg);
+ ret = spi_async(devrec->spi, &devrec->tx_post_msg);
if (ret)
- dev_err(printdev(devrec),
- "SPI write Failed for long register 0x%hx\n", reg);
-
- mutex_unlock(&devrec->buffer_mutex);
- return ret;
+ dev_err(printdev(devrec), "SPI write Failed for transmit buf\n");
}
/* This function relies on an undocumented write method. Once a write command
static int write_tx_buf(struct mrf24j40 *devrec, u16 reg,
const u8 *data, size_t length)
{
- int ret;
u16 cmd;
- u8 lengths[2];
- struct spi_message msg;
- struct spi_transfer addr_xfer = {
- .len = 2,
- .tx_buf = devrec->buf,
- };
- struct spi_transfer lengths_xfer = {
- .len = 2,
- .tx_buf = &lengths, /* TODO: Is DMA really required for SPI? */
- };
- struct spi_transfer data_xfer = {
- .len = length,
- .tx_buf = data,
- };
+ int ret;
/* Range check the length. 2 bytes are used for the length fields.*/
if (length > TX_FIFO_SIZE-2) {
length = TX_FIFO_SIZE-2;
}
- spi_message_init(&msg);
- spi_message_add_tail(&addr_xfer, &msg);
- spi_message_add_tail(&lengths_xfer, &msg);
- spi_message_add_tail(&data_xfer, &msg);
-
cmd = MRF24J40_WRITELONG(reg);
- mutex_lock(&devrec->buffer_mutex);
- devrec->buf[0] = cmd >> 8 & 0xff;
- devrec->buf[1] = cmd & 0xff;
- lengths[0] = 0x0; /* Header Length. Set to 0 for now. TODO */
- lengths[1] = length; /* Total length */
-
- ret = spi_sync(devrec->spi, &msg);
+ devrec->tx_hdr_buf[0] = cmd >> 8 & 0xff;
+ devrec->tx_hdr_buf[1] = cmd & 0xff;
+ devrec->tx_len_buf[0] = 0x0; /* Header Length. Set to 0 for now. TODO */
+ devrec->tx_len_buf[1] = length; /* Total length */
+ devrec->tx_buf_trx.tx_buf = data;
+ devrec->tx_buf_trx.len = length;
+
+ ret = spi_async(devrec->spi, &devrec->tx_msg);
if (ret)
dev_err(printdev(devrec), "SPI write Failed for TX buf\n");
- mutex_unlock(&devrec->buffer_mutex);
- return ret;
-}
-
-static int mrf24j40_read_rx_buf(struct mrf24j40 *devrec,
- u8 *data, u8 *len, u8 *lqi)
-{
- u8 rx_len;
- u8 addr[2];
- u8 lqi_rssi[2];
- u16 cmd;
- int ret;
- struct spi_message msg;
- struct spi_transfer addr_xfer = {
- .len = 2,
- .tx_buf = &addr,
- };
- struct spi_transfer data_xfer = {
- .len = 0x0, /* set below */
- .rx_buf = data,
- };
- struct spi_transfer status_xfer = {
- .len = 2,
- .rx_buf = &lqi_rssi,
- };
-
- /* Get the length of the data in the RX FIFO. The length in this
- * register exclues the 1-byte length field at the beginning. */
- ret = read_long_reg(devrec, REG_RX_FIFO, &rx_len);
- if (ret)
- goto out;
-
- /* Range check the RX FIFO length, accounting for the one-byte
- * length field at the beginning. */
- if (rx_len > RX_FIFO_SIZE-1) {
- dev_err(printdev(devrec), "Invalid length read from device. Performing short read.\n");
- rx_len = RX_FIFO_SIZE-1;
- }
-
- if (rx_len > *len) {
- /* Passed in buffer wasn't big enough. Should never happen. */
- dev_err(printdev(devrec), "Buffer not big enough. Performing short read\n");
- rx_len = *len;
- }
-
- /* Set up the commands to read the data. */
- cmd = MRF24J40_READLONG(REG_RX_FIFO+1);
- addr[0] = cmd >> 8 & 0xff;
- addr[1] = cmd & 0xff;
- data_xfer.len = rx_len;
-
- spi_message_init(&msg);
- spi_message_add_tail(&addr_xfer, &msg);
- spi_message_add_tail(&data_xfer, &msg);
- spi_message_add_tail(&status_xfer, &msg);
-
- ret = spi_sync(devrec->spi, &msg);
- if (ret) {
- dev_err(printdev(devrec), "SPI RX Buffer Read Failed.\n");
- goto out;
- }
-
- *lqi = lqi_rssi[0];
- *len = rx_len;
-
-#ifdef DEBUG
- print_hex_dump(KERN_DEBUG, "mrf24j40 rx: ",
- DUMP_PREFIX_OFFSET, 16, 1, data, *len, 0);
- pr_debug("mrf24j40 rx: lqi: %02hhx rssi: %02hhx\n",
- lqi_rssi[0], lqi_rssi[1]);
-#endif
-
-out:
return ret;
}
static int mrf24j40_tx(struct ieee802154_hw *hw, struct sk_buff *skb)
{
struct mrf24j40 *devrec = hw->priv;
- u8 val;
- int ret = 0;
dev_dbg(printdev(devrec), "tx packet of %d bytes\n", skb->len);
+ devrec->tx_skb = skb;
- ret = write_tx_buf(devrec, 0x000, skb->data, skb->len);
- if (ret)
- goto err;
-
- reinit_completion(&devrec->tx_complete);
-
- /* Set TXNTRIG bit of TXNCON to send packet */
- ret = read_short_reg(devrec, REG_TXNCON, &val);
- if (ret)
- goto err;
- val |= 0x1;
- /* Set TXNACKREQ if the ACK bit is set in the packet. */
- if (skb->data[0] & IEEE802154_FC_ACK_REQ)
- val |= 0x4;
- write_short_reg(devrec, REG_TXNCON, val);
-
- /* Wait for the device to send the TX complete interrupt. */
- ret = wait_for_completion_interruptible_timeout(
- &devrec->tx_complete,
- 5 * HZ);
- if (ret == -ERESTARTSYS)
- goto err;
- if (ret == 0) {
- dev_warn(printdev(devrec), "Timeout waiting for TX interrupt\n");
- ret = -ETIMEDOUT;
- goto err;
- }
-
- /* Check for send error from the device. */
- ret = read_short_reg(devrec, REG_TXSTAT, &val);
- if (ret)
- goto err;
- if (val & 0x1) {
- dev_dbg(printdev(devrec), "Error Sending. Retry count exceeded\n");
- ret = -ECOMM; /* TODO: Better error code ? */
- } else
- dev_dbg(printdev(devrec), "Packet Sent\n");
-
-err:
-
- return ret;
+ return write_tx_buf(devrec, 0x000, skb->data, skb->len);
}
static int mrf24j40_ed(struct ieee802154_hw *hw, u8 *level)
static int mrf24j40_start(struct ieee802154_hw *hw)
{
struct mrf24j40 *devrec = hw->priv;
- u8 val;
- int ret;
dev_dbg(printdev(devrec), "start\n");
- ret = read_short_reg(devrec, REG_INTCON, &val);
- if (ret)
- return ret;
- val &= ~(0x1|0x8); /* Clear TXNIE and RXIE. Enable interrupts */
- write_short_reg(devrec, REG_INTCON, val);
-
- return 0;
+ /* Clear TXNIE and RXIE. Enable interrupts */
+ return regmap_update_bits(devrec->regmap_short, REG_INTCON,
+ BIT_TXNIE | BIT_RXIE, 0);
}
static void mrf24j40_stop(struct ieee802154_hw *hw)
{
struct mrf24j40 *devrec = hw->priv;
- u8 val;
- int ret;
dev_dbg(printdev(devrec), "stop\n");
- ret = read_short_reg(devrec, REG_INTCON, &val);
- if (ret)
- return;
- val |= 0x1|0x8; /* Set TXNIE and RXIE. Disable Interrupts */
- write_short_reg(devrec, REG_INTCON, val);
+ /* Set TXNIE and RXIE. Disable Interrupts */
+ regmap_update_bits(devrec->regmap_short, REG_INTCON,
+ BIT_TXNIE | BIT_TXNIE, BIT_TXNIE | BIT_TXNIE);
}
static int mrf24j40_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
WARN_ON(channel > MRF24J40_CHAN_MAX);
/* Set Channel TODO */
- val = (channel-11) << 4 | 0x03;
- write_long_reg(devrec, REG_RFCON0, val);
+ val = (channel - 11) << RFCON0_CH_SHIFT | RFOPT_RECOMMEND;
+ ret = regmap_update_bits(devrec->regmap_long, REG_RFCON0,
+ RFCON0_CH_MASK, val);
+ if (ret)
+ return ret;
/* RF Reset */
- ret = read_short_reg(devrec, REG_RFCTL, &val);
+ ret = regmap_update_bits(devrec->regmap_short, REG_RFCTL, BIT_RFRST,
+ BIT_RFRST);
if (ret)
return ret;
- val |= 0x04;
- write_short_reg(devrec, REG_RFCTL, val);
- val &= ~0x04;
- write_short_reg(devrec, REG_RFCTL, val);
- udelay(SET_CHANNEL_DELAY_US); /* per datasheet */
+ ret = regmap_update_bits(devrec->regmap_short, REG_RFCTL, BIT_RFRST, 0);
+ if (!ret)
+ udelay(SET_CHANNEL_DELAY_US); /* per datasheet */
- return 0;
+ return ret;
}
static int mrf24j40_filter(struct ieee802154_hw *hw,
addrh = le16_to_cpu(filt->short_addr) >> 8 & 0xff;
addrl = le16_to_cpu(filt->short_addr) & 0xff;
- write_short_reg(devrec, REG_SADRH, addrh);
- write_short_reg(devrec, REG_SADRL, addrl);
+ regmap_write(devrec->regmap_short, REG_SADRH, addrh);
+ regmap_write(devrec->regmap_short, REG_SADRL, addrl);
dev_dbg(printdev(devrec),
"Set short addr to %04hx\n", filt->short_addr);
}
memcpy(addr, &filt->ieee_addr, 8);
for (i = 0; i < 8; i++)
- write_short_reg(devrec, REG_EADR0 + i, addr[i]);
+ regmap_write(devrec->regmap_short, REG_EADR0 + i,
+ addr[i]);
#ifdef DEBUG
pr_debug("Set long addr to: ");
panidh = le16_to_cpu(filt->pan_id) >> 8 & 0xff;
panidl = le16_to_cpu(filt->pan_id) & 0xff;
- write_short_reg(devrec, REG_PANIDH, panidh);
- write_short_reg(devrec, REG_PANIDL, panidl);
+ regmap_write(devrec->regmap_short, REG_PANIDH, panidh);
+ regmap_write(devrec->regmap_short, REG_PANIDL, panidl);
dev_dbg(printdev(devrec), "Set PANID to %04hx\n", filt->pan_id);
}
u8 val;
int ret;
- ret = read_short_reg(devrec, REG_RXMCR, &val);
- if (ret)
- return ret;
if (filt->pan_coord)
- val |= 0x8;
+ val = BIT_PANCOORD;
else
- val &= ~0x8;
- write_short_reg(devrec, REG_RXMCR, val);
+ val = 0;
+ ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR,
+ BIT_PANCOORD, val);
+ if (ret)
+ return ret;
/* REG_SLOTTED is maintained as default (unslotted/CSMA-CA).
* REG_ORDER is maintained as default (no beacon/superframe).
return 0;
}
-static int mrf24j40_handle_rx(struct mrf24j40 *devrec)
+static void mrf24j40_handle_rx_read_buf_unlock(struct mrf24j40 *devrec)
{
- u8 len = RX_FIFO_SIZE;
- u8 lqi = 0;
- u8 val;
- int ret = 0;
- int ret2;
- struct sk_buff *skb;
+ int ret;
- /* Turn off reception of packets off the air. This prevents the
- * device from overwriting the buffer while we're reading it. */
- ret = read_short_reg(devrec, REG_BBREG1, &val);
+ /* Turn back on reception of packets off the air. */
+ devrec->rx_msg.complete = NULL;
+ devrec->rx_buf[0] = MRF24J40_WRITESHORT(REG_BBREG1);
+ devrec->rx_buf[1] = 0x00; /* CLR RXDECINV */
+ ret = spi_async(devrec->spi, &devrec->rx_msg);
if (ret)
- goto out;
- val |= 4; /* SET RXDECINV */
- write_short_reg(devrec, REG_BBREG1, val);
+ dev_err(printdev(devrec), "failed to unlock rx buffer\n");
+}
+
+static void mrf24j40_handle_rx_read_buf_complete(void *context)
+{
+ struct mrf24j40 *devrec = context;
+ u8 len = devrec->rx_buf[2];
+ u8 rx_local_buf[RX_FIFO_SIZE];
+ struct sk_buff *skb;
+
+ memcpy(rx_local_buf, devrec->rx_fifo_buf, len);
+ mrf24j40_handle_rx_read_buf_unlock(devrec);
- skb = dev_alloc_skb(len);
+ skb = dev_alloc_skb(IEEE802154_MTU);
if (!skb) {
- ret = -ENOMEM;
- goto out;
+ dev_err(printdev(devrec), "failed to allocate skb\n");
+ return;
}
- ret = mrf24j40_read_rx_buf(devrec, skb_put(skb, len), &len, &lqi);
- if (ret < 0) {
- dev_err(printdev(devrec), "Failure reading RX FIFO\n");
- kfree_skb(skb);
- ret = -EINVAL;
- goto out;
+ memcpy(skb_put(skb, len), rx_local_buf, len);
+ ieee802154_rx_irqsafe(devrec->hw, skb, 0);
+
+#ifdef DEBUG
+ print_hex_dump(KERN_DEBUG, "mrf24j40 rx: ", DUMP_PREFIX_OFFSET, 16, 1,
+ rx_local_buf, len, 0);
+ pr_debug("mrf24j40 rx: lqi: %02hhx rssi: %02hhx\n",
+ devrec->rx_lqi_buf[0], devrec->rx_lqi_buf[1]);
+#endif
+}
+
+static void mrf24j40_handle_rx_read_buf(void *context)
+{
+ struct mrf24j40 *devrec = context;
+ u16 cmd;
+ int ret;
+
+ /* if length is invalid read the full MTU */
+ if (!ieee802154_is_valid_psdu_len(devrec->rx_buf[2]))
+ devrec->rx_buf[2] = IEEE802154_MTU;
+
+ cmd = MRF24J40_READLONG(REG_RX_FIFO + 1);
+ devrec->rx_addr_buf[0] = cmd >> 8 & 0xff;
+ devrec->rx_addr_buf[1] = cmd & 0xff;
+ devrec->rx_fifo_buf_trx.len = devrec->rx_buf[2];
+ ret = spi_async(devrec->spi, &devrec->rx_buf_msg);
+ if (ret) {
+ dev_err(printdev(devrec), "failed to read rx buffer\n");
+ mrf24j40_handle_rx_read_buf_unlock(devrec);
}
+}
- /* Cut off the checksum */
- skb_trim(skb, len-2);
+static void mrf24j40_handle_rx_read_len(void *context)
+{
+ struct mrf24j40 *devrec = context;
+ u16 cmd;
+ int ret;
- /* TODO: Other drivers call ieee20154_rx_irqsafe() here (eg: cc2040,
- * also from a workqueue). I think irqsafe is not necessary here.
- * Can someone confirm? */
- ieee802154_rx_irqsafe(devrec->hw, skb, lqi);
+ /* read the length of received frame */
+ devrec->rx_msg.complete = mrf24j40_handle_rx_read_buf;
+ devrec->rx_trx.len = 3;
+ cmd = MRF24J40_READLONG(REG_RX_FIFO);
+ devrec->rx_buf[0] = cmd >> 8 & 0xff;
+ devrec->rx_buf[1] = cmd & 0xff;
- dev_dbg(printdev(devrec), "RX Handled\n");
+ ret = spi_async(devrec->spi, &devrec->rx_msg);
+ if (ret) {
+ dev_err(printdev(devrec), "failed to read rx buffer length\n");
+ mrf24j40_handle_rx_read_buf_unlock(devrec);
+ }
+}
-out:
- /* Turn back on reception of packets off the air. */
- ret2 = read_short_reg(devrec, REG_BBREG1, &val);
- if (ret2)
- return ret2;
- val &= ~0x4; /* Clear RXDECINV */
- write_short_reg(devrec, REG_BBREG1, val);
+static int mrf24j40_handle_rx(struct mrf24j40 *devrec)
+{
+ /* Turn off reception of packets off the air. This prevents the
+ * device from overwriting the buffer while we're reading it.
+ */
+ devrec->rx_msg.complete = mrf24j40_handle_rx_read_len;
+ devrec->rx_trx.len = 2;
+ devrec->rx_buf[0] = MRF24J40_WRITESHORT(REG_BBREG1);
+ devrec->rx_buf[1] = BIT_RXDECINV; /* SET RXDECINV */
+
+ return spi_async(devrec->spi, &devrec->rx_msg);
+}
+
+static int
+mrf24j40_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be,
+ u8 retries)
+{
+ struct mrf24j40 *devrec = hw->priv;
+ u8 val;
+
+ /* min_be */
+ val = min_be << TXMCR_MIN_BE_SHIFT;
+ /* csma backoffs */
+ val |= retries << TXMCR_CSMA_RETRIES_SHIFT;
+
+ return regmap_update_bits(devrec->regmap_short, REG_TXMCR,
+ TXMCR_MIN_BE_MASK | TXMCR_CSMA_RETRIES_MASK,
+ val);
+}
+
+static int mrf24j40_set_cca_mode(struct ieee802154_hw *hw,
+ const struct wpan_phy_cca *cca)
+{
+ struct mrf24j40 *devrec = hw->priv;
+ u8 val;
+
+ /* mapping 802.15.4 to driver spec */
+ switch (cca->mode) {
+ case NL802154_CCA_ENERGY:
+ val = 2;
+ break;
+ case NL802154_CCA_CARRIER:
+ val = 1;
+ break;
+ case NL802154_CCA_ENERGY_CARRIER:
+ switch (cca->opt) {
+ case NL802154_CCA_OPT_ENERGY_CARRIER_AND:
+ val = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(devrec->regmap_short, REG_BBREG2,
+ BBREG2_CCA_MODE_MASK,
+ val << BBREG2_CCA_MODE_SHIFT);
+}
+
+/* array for representing ed levels */
+static const s32 mrf24j40_ed_levels[] = {
+ -9000, -8900, -8800, -8700, -8600, -8500, -8400, -8300, -8200, -8100,
+ -8000, -7900, -7800, -7700, -7600, -7500, -7400, -7300, -7200, -7100,
+ -7000, -6900, -6800, -6700, -6600, -6500, -6400, -6300, -6200, -6100,
+ -6000, -5900, -5800, -5700, -5600, -5500, -5400, -5300, -5200, -5100,
+ -5000, -4900, -4800, -4700, -4600, -4500, -4400, -4300, -4200, -4100,
+ -4000, -3900, -3800, -3700, -3600, -3500
+};
+
+/* map ed levels to register value */
+static const s32 mrf24j40_ed_levels_map[][2] = {
+ { -9000, 0 }, { -8900, 1 }, { -8800, 2 }, { -8700, 5 }, { -8600, 9 },
+ { -8500, 13 }, { -8400, 18 }, { -8300, 23 }, { -8200, 27 },
+ { -8100, 32 }, { -8000, 37 }, { -7900, 43 }, { -7800, 48 },
+ { -7700, 53 }, { -7600, 58 }, { -7500, 63 }, { -7400, 68 },
+ { -7300, 73 }, { -7200, 78 }, { -7100, 83 }, { -7000, 89 },
+ { -6900, 95 }, { -6800, 100 }, { -6700, 107 }, { -6600, 111 },
+ { -6500, 117 }, { -6400, 121 }, { -6300, 125 }, { -6200, 129 },
+ { -6100, 133 }, { -6000, 138 }, { -5900, 143 }, { -5800, 148 },
+ { -5700, 153 }, { -5600, 159 }, { -5500, 165 }, { -5400, 170 },
+ { -5300, 176 }, { -5200, 183 }, { -5100, 188 }, { -5000, 193 },
+ { -4900, 198 }, { -4800, 203 }, { -4700, 207 }, { -4600, 212 },
+ { -4500, 216 }, { -4400, 221 }, { -4300, 225 }, { -4200, 228 },
+ { -4100, 233 }, { -4000, 239 }, { -3900, 245 }, { -3800, 250 },
+ { -3700, 253 }, { -3600, 254 }, { -3500, 255 },
+};
+
+static int mrf24j40_set_cca_ed_level(struct ieee802154_hw *hw, s32 mbm)
+{
+ struct mrf24j40 *devrec = hw->priv;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mrf24j40_ed_levels_map); i++) {
+ if (mrf24j40_ed_levels_map[i][0] == mbm)
+ return regmap_write(devrec->regmap_short, REG_CCAEDTH,
+ mrf24j40_ed_levels_map[i][1]);
+ }
+
+ return -EINVAL;
+}
+
+static const s32 mrf24j40ma_powers[] = {
+ 0, -50, -120, -190, -280, -370, -490, -630, -1000, -1050, -1120, -1190,
+ -1280, -1370, -1490, -1630, -2000, -2050, -2120, -2190, -2280, -2370,
+ -2490, -2630, -3000, -3050, -3120, -3190, -3280, -3370, -3490, -3630,
+};
+
+static int mrf24j40_set_txpower(struct ieee802154_hw *hw, s32 mbm)
+{
+ struct mrf24j40 *devrec = hw->priv;
+ s32 small_scale;
+ u8 val;
+
+ if (0 >= mbm && mbm > -1000) {
+ val = TXPWRL_0 << TXPWRL_SHIFT;
+ small_scale = mbm;
+ } else if (-1000 >= mbm && mbm > -2000) {
+ val = TXPWRL_10 << TXPWRL_SHIFT;
+ small_scale = mbm + 1000;
+ } else if (-2000 >= mbm && mbm > -3000) {
+ val = TXPWRL_20 << TXPWRL_SHIFT;
+ small_scale = mbm + 2000;
+ } else if (-3000 >= mbm && mbm > -4000) {
+ val = TXPWRL_30 << TXPWRL_SHIFT;
+ small_scale = mbm + 3000;
+ } else {
+ return -EINVAL;
+ }
+
+ switch (small_scale) {
+ case 0:
+ val |= (TXPWRS_0 << TXPWRS_SHIFT);
+ break;
+ case -50:
+ val |= (TXPWRS_0_5 << TXPWRS_SHIFT);
+ break;
+ case -120:
+ val |= (TXPWRS_1_2 << TXPWRS_SHIFT);
+ break;
+ case -190:
+ val |= (TXPWRS_1_9 << TXPWRS_SHIFT);
+ break;
+ case -280:
+ val |= (TXPWRS_2_8 << TXPWRS_SHIFT);
+ break;
+ case -370:
+ val |= (TXPWRS_3_7 << TXPWRS_SHIFT);
+ break;
+ case -490:
+ val |= (TXPWRS_4_9 << TXPWRS_SHIFT);
+ break;
+ case -630:
+ val |= (TXPWRS_6_3 << TXPWRS_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(devrec->regmap_long, REG_RFCON3,
+ TXPWRL_MASK | TXPWRS_MASK, val);
+}
+
+static int mrf24j40_set_promiscuous_mode(struct ieee802154_hw *hw, bool on)
+{
+ struct mrf24j40 *devrec = hw->priv;
+ int ret;
+
+ if (on) {
+ /* set PROMI, ERRPKT and NOACKRSP */
+ ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR,
+ BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP,
+ BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP);
+ } else {
+ /* clear PROMI, ERRPKT and NOACKRSP */
+ ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR,
+ BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP,
+ 0);
+ }
return ret;
}
static const struct ieee802154_ops mrf24j40_ops = {
.owner = THIS_MODULE,
- .xmit_sync = mrf24j40_tx,
+ .xmit_async = mrf24j40_tx,
.ed = mrf24j40_ed,
.start = mrf24j40_start,
.stop = mrf24j40_stop,
.set_channel = mrf24j40_set_channel,
.set_hw_addr_filt = mrf24j40_filter,
+ .set_csma_params = mrf24j40_csma_params,
+ .set_cca_mode = mrf24j40_set_cca_mode,
+ .set_cca_ed_level = mrf24j40_set_cca_ed_level,
+ .set_txpower = mrf24j40_set_txpower,
+ .set_promiscuous_mode = mrf24j40_set_promiscuous_mode,
};
-static irqreturn_t mrf24j40_isr(int irq, void *data)
+static void mrf24j40_intstat_complete(void *context)
{
- struct mrf24j40 *devrec = data;
- u8 intstat;
- int ret;
+ struct mrf24j40 *devrec = context;
+ u8 intstat = devrec->irq_buf[1];
- /* Read the interrupt status */
- ret = read_short_reg(devrec, REG_INTSTAT, &intstat);
- if (ret)
- goto out;
+ enable_irq(devrec->spi->irq);
/* Check for TX complete */
- if (intstat & 0x1)
- complete(&devrec->tx_complete);
+ if (intstat & BIT_TXNIF)
+ ieee802154_xmit_complete(devrec->hw, devrec->tx_skb, false);
/* Check for Rx */
- if (intstat & 0x8)
+ if (intstat & BIT_RXIF)
mrf24j40_handle_rx(devrec);
+}
+
+static irqreturn_t mrf24j40_isr(int irq, void *data)
+{
+ struct mrf24j40 *devrec = data;
+ int ret;
+
+ disable_irq_nosync(irq);
+
+ devrec->irq_buf[0] = MRF24J40_READSHORT(REG_INTSTAT);
+ /* Read the interrupt status */
+ ret = spi_async(devrec->spi, &devrec->irq_msg);
+ if (ret) {
+ enable_irq(irq);
+ return IRQ_NONE;
+ }
-out:
return IRQ_HANDLED;
}
static int mrf24j40_hw_init(struct mrf24j40 *devrec)
{
+ u32 irq_type;
int ret;
- u8 val;
/* Initialize the device.
From datasheet section 3.2: Initialization. */
- ret = write_short_reg(devrec, REG_SOFTRST, 0x07);
+ ret = regmap_write(devrec->regmap_short, REG_SOFTRST, 0x07);
if (ret)
goto err_ret;
- ret = write_short_reg(devrec, REG_PACON2, 0x98);
+ ret = regmap_write(devrec->regmap_short, REG_PACON2, 0x98);
if (ret)
goto err_ret;
- ret = write_short_reg(devrec, REG_TXSTBL, 0x95);
+ ret = regmap_write(devrec->regmap_short, REG_TXSTBL, 0x95);
if (ret)
goto err_ret;
- ret = write_long_reg(devrec, REG_RFCON0, 0x03);
+ ret = regmap_write(devrec->regmap_long, REG_RFCON0, 0x03);
if (ret)
goto err_ret;
- ret = write_long_reg(devrec, REG_RFCON1, 0x01);
+ ret = regmap_write(devrec->regmap_long, REG_RFCON1, 0x01);
if (ret)
goto err_ret;
- ret = write_long_reg(devrec, REG_RFCON2, 0x80);
+ ret = regmap_write(devrec->regmap_long, REG_RFCON2, 0x80);
if (ret)
goto err_ret;
- ret = write_long_reg(devrec, REG_RFCON6, 0x90);
+ ret = regmap_write(devrec->regmap_long, REG_RFCON6, 0x90);
if (ret)
goto err_ret;
- ret = write_long_reg(devrec, REG_RFCON7, 0x80);
+ ret = regmap_write(devrec->regmap_long, REG_RFCON7, 0x80);
if (ret)
goto err_ret;
- ret = write_long_reg(devrec, REG_RFCON8, 0x10);
+ ret = regmap_write(devrec->regmap_long, REG_RFCON8, 0x10);
if (ret)
goto err_ret;
- ret = write_long_reg(devrec, REG_SLPCON1, 0x21);
+ ret = regmap_write(devrec->regmap_long, REG_SLPCON1, 0x21);
if (ret)
goto err_ret;
- ret = write_short_reg(devrec, REG_BBREG2, 0x80);
+ ret = regmap_write(devrec->regmap_short, REG_BBREG2, 0x80);
if (ret)
goto err_ret;
- ret = write_short_reg(devrec, REG_CCAEDTH, 0x60);
+ ret = regmap_write(devrec->regmap_short, REG_CCAEDTH, 0x60);
if (ret)
goto err_ret;
- ret = write_short_reg(devrec, REG_BBREG6, 0x40);
+ ret = regmap_write(devrec->regmap_short, REG_BBREG6, 0x40);
if (ret)
goto err_ret;
- ret = write_short_reg(devrec, REG_RFCTL, 0x04);
+ ret = regmap_write(devrec->regmap_short, REG_RFCTL, 0x04);
if (ret)
goto err_ret;
- ret = write_short_reg(devrec, REG_RFCTL, 0x0);
+ ret = regmap_write(devrec->regmap_short, REG_RFCTL, 0x0);
if (ret)
goto err_ret;
udelay(192);
/* Set RX Mode. RXMCR<1:0>: 0x0 normal, 0x1 promisc, 0x2 error */
- ret = read_short_reg(devrec, REG_RXMCR, &val);
- if (ret)
- goto err_ret;
-
- val &= ~0x3; /* Clear RX mode (normal) */
-
- ret = write_short_reg(devrec, REG_RXMCR, val);
+ ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, 0x03, 0x00);
if (ret)
goto err_ret;
/* Enable external amplifier.
* From MRF24J40MC datasheet section 1.3: Operation.
*/
- read_long_reg(devrec, REG_TESTMODE, &val);
- val |= 0x7; /* Configure GPIO 0-2 to control amplifier */
- write_long_reg(devrec, REG_TESTMODE, val);
+ regmap_update_bits(devrec->regmap_long, REG_TESTMODE, 0x07,
+ 0x07);
- read_short_reg(devrec, REG_TRISGPIO, &val);
- val |= 0x8; /* Set GPIO3 as output. */
- write_short_reg(devrec, REG_TRISGPIO, val);
+ /* Set GPIO3 as output. */
+ regmap_update_bits(devrec->regmap_short, REG_TRISGPIO, 0x08,
+ 0x08);
- read_short_reg(devrec, REG_GPIO, &val);
- val |= 0x8; /* Set GPIO3 HIGH to enable U5 voltage regulator */
- write_short_reg(devrec, REG_GPIO, val);
+ /* Set GPIO3 HIGH to enable U5 voltage regulator */
+ regmap_update_bits(devrec->regmap_short, REG_GPIO, 0x08, 0x08);
/* Reduce TX pwr to meet FCC requirements.
* From MRF24J40MC datasheet section 3.1.1
*/
- write_long_reg(devrec, REG_RFCON3, 0x28);
+ regmap_write(devrec->regmap_long, REG_RFCON3, 0x28);
+ }
+
+ irq_type = irq_get_trigger_type(devrec->spi->irq);
+ if (irq_type == IRQ_TYPE_EDGE_RISING ||
+ irq_type == IRQ_TYPE_EDGE_FALLING)
+ dev_warn(&devrec->spi->dev,
+ "Using edge triggered irq's are not recommended, because it can cause races and result in a non-functional driver!\n");
+ switch (irq_type) {
+ case IRQ_TYPE_EDGE_RISING:
+ case IRQ_TYPE_LEVEL_HIGH:
+ /* set interrupt polarity to rising */
+ ret = regmap_update_bits(devrec->regmap_long, REG_SLPCON0,
+ BIT_INTEDGE, BIT_INTEDGE);
+ if (ret)
+ goto err_ret;
+ break;
+ default:
+ /* default is falling edge */
+ break;
}
return 0;
return ret;
}
-static int mrf24j40_probe(struct spi_device *spi)
+static void
+mrf24j40_setup_tx_spi_messages(struct mrf24j40 *devrec)
{
- int ret = -ENOMEM;
- struct mrf24j40 *devrec;
+ spi_message_init(&devrec->tx_msg);
+ devrec->tx_msg.context = devrec;
+ devrec->tx_msg.complete = write_tx_buf_complete;
+ devrec->tx_hdr_trx.len = 2;
+ devrec->tx_hdr_trx.tx_buf = devrec->tx_hdr_buf;
+ spi_message_add_tail(&devrec->tx_hdr_trx, &devrec->tx_msg);
+ devrec->tx_len_trx.len = 2;
+ devrec->tx_len_trx.tx_buf = devrec->tx_len_buf;
+ spi_message_add_tail(&devrec->tx_len_trx, &devrec->tx_msg);
+ spi_message_add_tail(&devrec->tx_buf_trx, &devrec->tx_msg);
+
+ spi_message_init(&devrec->tx_post_msg);
+ devrec->tx_post_msg.context = devrec;
+ devrec->tx_post_trx.len = 2;
+ devrec->tx_post_trx.tx_buf = devrec->tx_post_buf;
+ spi_message_add_tail(&devrec->tx_post_trx, &devrec->tx_post_msg);
+}
- dev_info(&spi->dev, "probe(). IRQ: %d\n", spi->irq);
+static void
+mrf24j40_setup_rx_spi_messages(struct mrf24j40 *devrec)
+{
+ spi_message_init(&devrec->rx_msg);
+ devrec->rx_msg.context = devrec;
+ devrec->rx_trx.len = 2;
+ devrec->rx_trx.tx_buf = devrec->rx_buf;
+ devrec->rx_trx.rx_buf = devrec->rx_buf;
+ spi_message_add_tail(&devrec->rx_trx, &devrec->rx_msg);
+
+ spi_message_init(&devrec->rx_buf_msg);
+ devrec->rx_buf_msg.context = devrec;
+ devrec->rx_buf_msg.complete = mrf24j40_handle_rx_read_buf_complete;
+ devrec->rx_addr_trx.len = 2;
+ devrec->rx_addr_trx.tx_buf = devrec->rx_addr_buf;
+ spi_message_add_tail(&devrec->rx_addr_trx, &devrec->rx_buf_msg);
+ devrec->rx_fifo_buf_trx.rx_buf = devrec->rx_fifo_buf;
+ spi_message_add_tail(&devrec->rx_fifo_buf_trx, &devrec->rx_buf_msg);
+ devrec->rx_lqi_trx.len = 2;
+ devrec->rx_lqi_trx.rx_buf = devrec->rx_lqi_buf;
+ spi_message_add_tail(&devrec->rx_lqi_trx, &devrec->rx_buf_msg);
+}
- devrec = devm_kzalloc(&spi->dev, sizeof(struct mrf24j40), GFP_KERNEL);
- if (!devrec)
- goto err_ret;
- devrec->buf = devm_kzalloc(&spi->dev, 3, GFP_KERNEL);
- if (!devrec->buf)
- goto err_ret;
+static void
+mrf24j40_setup_irq_spi_messages(struct mrf24j40 *devrec)
+{
+ spi_message_init(&devrec->irq_msg);
+ devrec->irq_msg.context = devrec;
+ devrec->irq_msg.complete = mrf24j40_intstat_complete;
+ devrec->irq_trx.len = 2;
+ devrec->irq_trx.tx_buf = devrec->irq_buf;
+ devrec->irq_trx.rx_buf = devrec->irq_buf;
+ spi_message_add_tail(&devrec->irq_trx, &devrec->irq_msg);
+}
+
+static void mrf24j40_phy_setup(struct mrf24j40 *devrec)
+{
+ ieee802154_random_extended_addr(&devrec->hw->phy->perm_extended_addr);
+ devrec->hw->phy->current_channel = 11;
+
+ /* mrf24j40 supports max_minbe 0 - 3 */
+ devrec->hw->phy->supported.max_minbe = 3;
+ /* datasheet doesn't say anything about max_be, but we have min_be
+ * So we assume the max_be default.
+ */
+ devrec->hw->phy->supported.min_maxbe = 5;
+ devrec->hw->phy->supported.max_maxbe = 5;
+
+ devrec->hw->phy->cca.mode = NL802154_CCA_CARRIER;
+ devrec->hw->phy->supported.cca_modes = BIT(NL802154_CCA_ENERGY) |
+ BIT(NL802154_CCA_CARRIER) |
+ BIT(NL802154_CCA_ENERGY_CARRIER);
+ devrec->hw->phy->supported.cca_opts = BIT(NL802154_CCA_OPT_ENERGY_CARRIER_AND);
+
+ devrec->hw->phy->cca_ed_level = -6900;
+ devrec->hw->phy->supported.cca_ed_levels = mrf24j40_ed_levels;
+ devrec->hw->phy->supported.cca_ed_levels_size = ARRAY_SIZE(mrf24j40_ed_levels);
+
+ switch (spi_get_device_id(devrec->spi)->driver_data) {
+ case MRF24J40:
+ case MRF24J40MA:
+ devrec->hw->phy->supported.tx_powers = mrf24j40ma_powers;
+ devrec->hw->phy->supported.tx_powers_size = ARRAY_SIZE(mrf24j40ma_powers);
+ devrec->hw->phy->flags |= WPAN_PHY_FLAG_TXPOWER;
+ break;
+ default:
+ break;
+ }
+}
- spi->mode = SPI_MODE_0; /* TODO: Is this appropriate for right here? */
- if (spi->max_speed_hz > MAX_SPI_SPEED_HZ)
- spi->max_speed_hz = MAX_SPI_SPEED_HZ;
+static int mrf24j40_probe(struct spi_device *spi)
+{
+ int ret = -ENOMEM, irq_type;
+ struct ieee802154_hw *hw;
+ struct mrf24j40 *devrec;
- mutex_init(&devrec->buffer_mutex);
- init_completion(&devrec->tx_complete);
- devrec->spi = spi;
- spi_set_drvdata(spi, devrec);
+ dev_info(&spi->dev, "probe(). IRQ: %d\n", spi->irq);
/* Register with the 802154 subsystem */
- devrec->hw = ieee802154_alloc_hw(0, &mrf24j40_ops);
- if (!devrec->hw)
+ hw = ieee802154_alloc_hw(sizeof(*devrec), &mrf24j40_ops);
+ if (!hw)
goto err_ret;
- devrec->hw->priv = devrec;
- devrec->hw->parent = &devrec->spi->dev;
+ devrec = hw->priv;
+ devrec->spi = spi;
+ spi_set_drvdata(spi, devrec);
+ devrec->hw = hw;
+ devrec->hw->parent = &spi->dev;
devrec->hw->phy->supported.channels[0] = CHANNEL_MASK;
- devrec->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AFILT;
+ devrec->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT |
+ IEEE802154_HW_CSMA_PARAMS |
+ IEEE802154_HW_PROMISCUOUS;
+
+ devrec->hw->phy->flags = WPAN_PHY_FLAG_CCA_MODE |
+ WPAN_PHY_FLAG_CCA_ED_LEVEL;
+
+ mrf24j40_setup_tx_spi_messages(devrec);
+ mrf24j40_setup_rx_spi_messages(devrec);
+ mrf24j40_setup_irq_spi_messages(devrec);
+
+ devrec->regmap_short = devm_regmap_init_spi(spi,
+ &mrf24j40_short_regmap);
+ if (IS_ERR(devrec->regmap_short)) {
+ ret = PTR_ERR(devrec->regmap_short);
+ dev_err(&spi->dev, "Failed to allocate short register map: %d\n",
+ ret);
+ goto err_register_device;
+ }
- dev_dbg(printdev(devrec), "registered mrf24j40\n");
- ret = ieee802154_register_hw(devrec->hw);
- if (ret)
+ devrec->regmap_long = devm_regmap_init(&spi->dev,
+ &mrf24j40_long_regmap_bus,
+ spi, &mrf24j40_long_regmap);
+ if (IS_ERR(devrec->regmap_long)) {
+ ret = PTR_ERR(devrec->regmap_long);
+ dev_err(&spi->dev, "Failed to allocate long register map: %d\n",
+ ret);
goto err_register_device;
+ }
+
+ if (spi->max_speed_hz > MAX_SPI_SPEED_HZ) {
+ dev_warn(&spi->dev, "spi clock above possible maximum: %d",
+ MAX_SPI_SPEED_HZ);
+ return -EINVAL;
+ }
ret = mrf24j40_hw_init(devrec);
if (ret)
- goto err_hw_init;
+ goto err_register_device;
- ret = devm_request_threaded_irq(&spi->dev,
- spi->irq,
- NULL,
- mrf24j40_isr,
- IRQF_TRIGGER_LOW|IRQF_ONESHOT,
- dev_name(&spi->dev),
- devrec);
+ mrf24j40_phy_setup(devrec);
+ /* request IRQF_TRIGGER_LOW as fallback default */
+ irq_type = irq_get_trigger_type(spi->irq);
+ if (!irq_type)
+ irq_type = IRQF_TRIGGER_LOW;
+
+ ret = devm_request_irq(&spi->dev, spi->irq, mrf24j40_isr,
+ irq_type, dev_name(&spi->dev), devrec);
if (ret) {
dev_err(printdev(devrec), "Unable to get IRQ");
- goto err_irq;
+ goto err_register_device;
}
+ dev_dbg(printdev(devrec), "registered mrf24j40\n");
+ ret = ieee802154_register_hw(devrec->hw);
+ if (ret)
+ goto err_register_device;
+
return 0;
-err_irq:
-err_hw_init:
- ieee802154_unregister_hw(devrec->hw);
err_register_device:
ieee802154_free_hw(devrec->hw);
err_ret:
return 0;
}
+static const struct of_device_id mrf24j40_of_match[] = {
+ { .compatible = "microchip,mrf24j40", .data = (void *)MRF24J40 },
+ { .compatible = "microchip,mrf24j40ma", .data = (void *)MRF24J40MA },
+ { .compatible = "microchip,mrf24j40mc", .data = (void *)MRF24J40MC },
+ { },
+};
+MODULE_DEVICE_TABLE(of, mrf24j40_of_match);
+
static const struct spi_device_id mrf24j40_ids[] = {
{ "mrf24j40", MRF24J40 },
{ "mrf24j40ma", MRF24J40MA },
static struct spi_driver mrf24j40_driver = {
.driver = {
+ .of_match_table = of_match_ptr(mrf24j40_of_match),
.name = "mrf24j40",
- .owner = THIS_MODULE,
},
.id_table = mrf24j40_ids,
.probe = mrf24j40_probe,