cregit-Linux how code gets into the kernel

Release 4.10 drivers/bus/sunxi-rsb.c

Directory: drivers/bus
/*
 * RSB (Reduced Serial Bus) driver.
 *
 * Author: Chen-Yu Tsai <wens@csie.org>
 *
 * This file is licensed under the terms of the GNU General Public License
 * version 2.  This program is licensed "as is" without any warranty of any
 * kind, whether express or implied.
 *
 * The RSB controller looks like an SMBus controller which only supports
 * byte and word data transfers. But, it differs from standard SMBus
 * protocol on several aspects:
 * - it uses addresses set at runtime to address slaves. Runtime addresses
 *   are sent to slaves using their 12bit hardware addresses. Up to 15
 *   runtime addresses are available.
 * - it adds a parity bit every 8bits of data and address for read and
 *   write accesses; this replaces the ack bit
 * - only one read access is required to read a byte (instead of a write
 *   followed by a read access in standard SMBus protocol)
 * - there's no Ack bit after each read access
 *
 * This means this bus cannot be used to interface with standard SMBus
 * devices. Devices known to support this interface include the AXP223,
 * AXP809, and AXP806 PMICs, and the AC100 audio codec, all from X-Powers.
 *
 * A description of the operation and wire protocol can be found in the
 * RSB section of Allwinner's A80 user manual, which can be found at
 *
 *     https://github.com/allwinner-zh/documents/tree/master/A80
 *
 * This document is officially released by Allwinner.
 *
 * This driver is based on i2c-sun6i-p2wi.c, the P2WI bus driver.
 *
 */

#include <linux/clk.h>
#include <linux/clk/clk-conf.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/sunxi-rsb.h>
#include <linux/types.h>

/* RSB registers */

#define RSB_CTRL	0x0	
/* Global control */

#define RSB_CCR		0x4	
/* Clock control */

#define RSB_INTE	0x8	
/* Interrupt controls */

#define RSB_INTS	0xc	
/* Interrupt status */

#define RSB_ADDR	0x10	
/* Address to send with read/write command */

#define RSB_DATA	0x1c	
/* Data to read/write */

#define RSB_LCR		0x24	
/* Line control */

#define RSB_DMCR	0x28	
/* Device mode (init) control */

#define RSB_CMD		0x2c	
/* RSB Command */

#define RSB_DAR		0x30	
/* Device address / runtime address */

/* CTRL fields */

#define RSB_CTRL_START_TRANS		BIT(7)

#define RSB_CTRL_ABORT_TRANS		BIT(6)

#define RSB_CTRL_GLOBAL_INT_ENB		BIT(1)

#define RSB_CTRL_SOFT_RST		BIT(0)

/* CLK CTRL fields */

#define RSB_CCR_SDA_OUT_DELAY(v)	(((v) & 0x7) << 8)

#define RSB_CCR_MAX_CLK_DIV		0xff

#define RSB_CCR_CLK_DIV(v)		((v) & RSB_CCR_MAX_CLK_DIV)

/* STATUS fields */

#define RSB_INTS_TRANS_ERR_ACK		BIT(16)

#define RSB_INTS_TRANS_ERR_DATA_BIT(v)	(((v) >> 8) & 0xf)

#define RSB_INTS_TRANS_ERR_DATA		GENMASK(11, 8)

#define RSB_INTS_LOAD_BSY		BIT(2)

#define RSB_INTS_TRANS_ERR		BIT(1)

#define RSB_INTS_TRANS_OVER		BIT(0)

/* LINE CTRL fields*/

#define RSB_LCR_SCL_STATE		BIT(5)

#define RSB_LCR_SDA_STATE		BIT(4)

#define RSB_LCR_SCL_CTL			BIT(3)

#define RSB_LCR_SCL_CTL_EN		BIT(2)

#define RSB_LCR_SDA_CTL			BIT(1)

#define RSB_LCR_SDA_CTL_EN		BIT(0)

/* DEVICE MODE CTRL field values */

#define RSB_DMCR_DEVICE_START		BIT(31)

#define RSB_DMCR_MODE_DATA		(0x7c << 16)

#define RSB_DMCR_MODE_REG		(0x3e << 8)

#define RSB_DMCR_DEV_ADDR		0x00

/* CMD values */

#define RSB_CMD_RD8			0x8b

#define RSB_CMD_RD16			0x9c

#define RSB_CMD_RD32			0xa6

#define RSB_CMD_WR8			0x4e

#define RSB_CMD_WR16			0x59

#define RSB_CMD_WR32			0x63

#define RSB_CMD_STRA			0xe8

/* DAR fields */

#define RSB_DAR_RTA(v)			(((v) & 0xff) << 16)

#define RSB_DAR_DA(v)			((v) & 0xffff)


#define RSB_MAX_FREQ			20000000


#define RSB_CTRL_NAME			"sunxi-rsb"


struct sunxi_rsb_addr_map {
	
u16 hwaddr;
	
u8 rtaddr;
};


struct sunxi_rsb {
	
struct device *dev;
	
void __iomem *regs;
	
struct clk *clk;
	
struct reset_control *rstc;
	
struct completion complete;
	
struct mutex lock;
	
unsigned int status;
};

/* bus / slave device related functions */

static struct bus_type sunxi_rsb_bus;


static int sunxi_rsb_device_match(struct device *dev, struct device_driver *drv) { return of_driver_match_device(dev, drv); }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai24100.00%1100.00%
Total24100.00%1100.00%


static int sunxi_rsb_device_probe(struct device *dev) { const struct sunxi_rsb_driver *drv = to_sunxi_rsb_driver(dev->driver); struct sunxi_rsb_device *rdev = to_sunxi_rsb_device(dev); int ret; if (!drv->probe) return -ENODEV; if (!rdev->irq) { int irq = -ENOENT; if (dev->of_node) irq = of_irq_get(dev->of_node, 0); if (irq == -EPROBE_DEFER) return irq; if (irq < 0) irq = 0; rdev->irq = irq; } ret = of_clk_set_defaults(dev->of_node, false); if (ret < 0) return ret; return drv->probe(rdev); }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai134100.00%1100.00%
Total134100.00%1100.00%


static int sunxi_rsb_device_remove(struct device *dev) { const struct sunxi_rsb_driver *drv = to_sunxi_rsb_driver(dev->driver); return drv->remove(to_sunxi_rsb_device(dev)); }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai35100.00%1100.00%
Total35100.00%1100.00%

static struct bus_type sunxi_rsb_bus = { .name = RSB_CTRL_NAME, .match = sunxi_rsb_device_match, .probe = sunxi_rsb_device_probe, .remove = sunxi_rsb_device_remove, };
static void sunxi_rsb_dev_release(struct device *dev) { struct sunxi_rsb_device *rdev = to_sunxi_rsb_device(dev); kfree(rdev); }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai26100.00%1100.00%
Total26100.00%1100.00%

/** * sunxi_rsb_device_create() - allocate and add an RSB device * @rsb: RSB controller * @node: RSB slave device node * @hwaddr: RSB slave hardware address * @rtaddr: RSB slave runtime address */
static struct sunxi_rsb_device *sunxi_rsb_device_create(struct sunxi_rsb *rsb, struct device_node *node, u16 hwaddr, u8 rtaddr) { int err; struct sunxi_rsb_device *rdev; rdev = kzalloc(sizeof(*rdev), GFP_KERNEL); if (!rdev) return ERR_PTR(-ENOMEM); rdev->rsb = rsb; rdev->hwaddr = hwaddr; rdev->rtaddr = rtaddr; rdev->dev.bus = &sunxi_rsb_bus; rdev->dev.parent = rsb->dev; rdev->dev.of_node = node; rdev->dev.release = sunxi_rsb_dev_release; dev_set_name(&rdev->dev, "%s-%x", RSB_CTRL_NAME, hwaddr); err = device_register(&rdev->dev); if (err < 0) { dev_err(&rdev->dev, "Can't add %s, status %d\n", dev_name(&rdev->dev), err); goto err_device_add; } dev_dbg(&rdev->dev, "device %s registered\n", dev_name(&rdev->dev)); err_device_add: put_device(&rdev->dev); return ERR_PTR(err); }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai199100.00%1100.00%
Total199100.00%1100.00%

/** * sunxi_rsb_device_unregister(): unregister an RSB device * @rdev: rsb_device to be removed */
static void sunxi_rsb_device_unregister(struct sunxi_rsb_device *rdev) { device_unregister(&rdev->dev); }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai19100.00%1100.00%
Total19100.00%1100.00%


static int sunxi_rsb_remove_devices(struct device *dev, void *data) { struct sunxi_rsb_device *rdev = to_sunxi_rsb_device(dev); if (dev->bus == &sunxi_rsb_bus) sunxi_rsb_device_unregister(rdev); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai42100.00%1100.00%
Total42100.00%1100.00%

/** * sunxi_rsb_driver_register() - Register device driver with RSB core * @rdrv: device driver to be associated with slave-device. * * This API will register the client driver with the RSB framework. * It is typically called from the driver's module-init function. */
int sunxi_rsb_driver_register(struct sunxi_rsb_driver *rdrv) { rdrv->driver.bus = &sunxi_rsb_bus; return driver_register(&rdrv->driver); }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai28100.00%1100.00%
Total28100.00%1100.00%

EXPORT_SYMBOL_GPL(sunxi_rsb_driver_register); /* common code that starts a transfer */
static int _sunxi_rsb_run_xfer(struct sunxi_rsb *rsb) { if (readl(rsb->regs + RSB_CTRL) & RSB_CTRL_START_TRANS) { dev_dbg(rsb->dev, "RSB transfer still in progress\n"); return -EBUSY; } reinit_completion(&rsb->complete); writel(RSB_INTS_LOAD_BSY | RSB_INTS_TRANS_ERR | RSB_INTS_TRANS_OVER, rsb->regs + RSB_INTE); writel(RSB_CTRL_START_TRANS | RSB_CTRL_GLOBAL_INT_ENB, rsb->regs + RSB_CTRL); if (!wait_for_completion_io_timeout(&rsb->complete, msecs_to_jiffies(100))) { dev_dbg(rsb->dev, "RSB timeout\n"); /* abort the transfer */ writel(RSB_CTRL_ABORT_TRANS, rsb->regs + RSB_CTRL); /* clear any interrupt flags */ writel(readl(rsb->regs + RSB_INTS), rsb->regs + RSB_INTS); return -ETIMEDOUT; } if (rsb->status & RSB_INTS_LOAD_BSY) { dev_dbg(rsb->dev, "RSB busy\n"); return -EBUSY; } if (rsb->status & RSB_INTS_TRANS_ERR) { if (rsb->status & RSB_INTS_TRANS_ERR_ACK) { dev_dbg(rsb->dev, "RSB slave nack\n"); return -EINVAL; } if (rsb->status & RSB_INTS_TRANS_ERR_DATA) { dev_dbg(rsb->dev, "RSB transfer data error\n"); return -EIO; } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai219100.00%1100.00%
Total219100.00%1100.00%


static int sunxi_rsb_read(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr, u32 *buf, size_t len) { u32 cmd; int ret; if (!buf) return -EINVAL; switch (len) { case 1: cmd = RSB_CMD_RD8; break; case 2: cmd = RSB_CMD_RD16; break; case 4: cmd = RSB_CMD_RD32; break; default: dev_err(rsb->dev, "Invalid access width: %zd\n", len); return -EINVAL; } mutex_lock(&rsb->lock); writel(addr, rsb->regs + RSB_ADDR); writel(RSB_DAR_RTA(rtaddr), rsb->regs + RSB_DAR); writel(cmd, rsb->regs + RSB_CMD); ret = _sunxi_rsb_run_xfer(rsb); if (ret) goto unlock; *buf = readl(rsb->regs + RSB_DATA); unlock: mutex_unlock(&rsb->lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai16497.62%133.33%
dan carpenterdan carpenter31.79%133.33%
andre przywaraandre przywara10.60%133.33%
Total168100.00%3100.00%


static int sunxi_rsb_write(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr, const u32 *buf, size_t len) { u32 cmd; int ret; if (!buf) return -EINVAL; switch (len) { case 1: cmd = RSB_CMD_WR8; break; case 2: cmd = RSB_CMD_WR16; break; case 4: cmd = RSB_CMD_WR32; break; default: dev_err(rsb->dev, "Invalid access width: %zd\n", len); return -EINVAL; } mutex_lock(&rsb->lock); writel(addr, rsb->regs + RSB_ADDR); writel(RSB_DAR_RTA(rtaddr), rsb->regs + RSB_DAR); writel(*buf, rsb->regs + RSB_DATA); writel(cmd, rsb->regs + RSB_CMD); ret = _sunxi_rsb_run_xfer(rsb); mutex_unlock(&rsb->lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai15999.38%150.00%
andre przywaraandre przywara10.62%150.00%
Total160100.00%2100.00%

/* RSB regmap functions */ struct sunxi_rsb_ctx { struct sunxi_rsb_device *rdev; int size; };
static int regmap_sunxi_rsb_reg_read(void *context, unsigned int reg, unsigned int *val) { struct sunxi_rsb_ctx *ctx = context; struct sunxi_rsb_device *rdev = ctx->rdev; if (reg > 0xff) return -EINVAL; return sunxi_rsb_read(rdev->rsb, rdev->rtaddr, reg, val, ctx->size); }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai65100.00%1100.00%
Total65100.00%1100.00%


static int regmap_sunxi_rsb_reg_write(void *context, unsigned int reg, unsigned int val) { struct sunxi_rsb_ctx *ctx = context; struct sunxi_rsb_device *rdev = ctx->rdev; return sunxi_rsb_write(rdev->rsb, rdev->rtaddr, reg, &val, ctx->size); }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai55100.00%1100.00%
Total55100.00%1100.00%


static void regmap_sunxi_rsb_free_ctx(void *context) { struct sunxi_rsb_ctx *ctx = context; kfree(ctx); }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai22100.00%1100.00%
Total22100.00%1100.00%

static struct regmap_bus regmap_sunxi_rsb = { .reg_write = regmap_sunxi_rsb_reg_write, .reg_read = regmap_sunxi_rsb_reg_read, .free_context = regmap_sunxi_rsb_free_ctx, .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, .val_format_endian_default = REGMAP_ENDIAN_NATIVE, };
static struct sunxi_rsb_ctx *regmap_sunxi_rsb_init_ctx(struct sunxi_rsb_device *rdev, const struct regmap_config *config) { struct sunxi_rsb_ctx *ctx; switch (config->val_bits) { case 8: case 16: case 32: break; default: return ERR_PTR(-EINVAL); } ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return ERR_PTR(-ENOMEM); ctx->rdev = rdev; ctx->size = config->val_bits / 8; return ctx; }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai94100.00%1100.00%
Total94100.00%1100.00%


struct regmap *__devm_regmap_init_sunxi_rsb(struct sunxi_rsb_device *rdev, const struct regmap_config *config, struct lock_class_key *lock_key, const char *lock_name) { struct sunxi_rsb_ctx *ctx = regmap_sunxi_rsb_init_ctx(rdev, config); if (IS_ERR(ctx)) return ERR_CAST(ctx); return __devm_regmap_init(&rdev->dev, &regmap_sunxi_rsb, ctx, config, lock_key, lock_name); }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai73100.00%1100.00%
Total73100.00%1100.00%

EXPORT_SYMBOL_GPL(__devm_regmap_init_sunxi_rsb); /* RSB controller driver functions */
static irqreturn_t sunxi_rsb_irq(int irq, void *dev_id) { struct sunxi_rsb *rsb = dev_id; u32 status; status = readl(rsb->regs + RSB_INTS); rsb->status = status; /* Clear interrupts */ status &= (RSB_INTS_LOAD_BSY | RSB_INTS_TRANS_ERR | RSB_INTS_TRANS_OVER); writel(status, rsb->regs + RSB_INTS); complete(&rsb->complete); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai73100.00%1100.00%
Total73100.00%1100.00%


static int sunxi_rsb_init_device_mode(struct sunxi_rsb *rsb) { int ret = 0; u32 reg; /* send init sequence */ writel(RSB_DMCR_DEVICE_START | RSB_DMCR_MODE_DATA | RSB_DMCR_MODE_REG | RSB_DMCR_DEV_ADDR, rsb->regs + RSB_DMCR); readl_poll_timeout(rsb->regs + RSB_DMCR, reg, !(reg & RSB_DMCR_DEVICE_START), 100, 250000); if (reg & RSB_DMCR_DEVICE_START) ret = -ETIMEDOUT; /* clear interrupt status bits */ writel(readl(rsb->regs + RSB_INTS), rsb->regs + RSB_INTS); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai92100.00%1100.00%
Total92100.00%1100.00%

/* * There are 15 valid runtime addresses, though Allwinner typically * skips the first, for unknown reasons, and uses the following three. * * 0x17, 0x2d, 0x3a, 0x4e, 0x59, 0x63, 0x74, 0x8b, * 0x9c, 0xa6, 0xb1, 0xc5, 0xd2, 0xe8, 0xff * * No designs with 2 RSB slave devices sharing identical hardware * addresses on the same bus have been seen in the wild. All designs * use 0x2d for the primary PMIC, 0x3a for the secondary PMIC if * there is one, and 0x45 for peripheral ICs. * * The hardware does not seem to support re-setting runtime addresses. * Attempts to do so result in the slave devices returning a NACK. * Hence we just hardcode the mapping here, like Allwinner does. */ static const struct sunxi_rsb_addr_map sunxi_rsb_addr_maps[] = { { 0x3a3, 0x2d }, /* Primary PMIC: AXP223, AXP809, AXP81X, ... */ { 0x745, 0x3a }, /* Secondary PMIC: AXP806, ... */ { 0xe89, 0x4e }, /* Peripheral IC: AC100, ... */ };
static u8 sunxi_rsb_get_rtaddr(u16 hwaddr) { int i; for (i = 0; i < ARRAY_SIZE(sunxi_rsb_addr_maps); i++) if (hwaddr == sunxi_rsb_addr_maps[i].hwaddr) return sunxi_rsb_addr_maps[i].rtaddr; return 0; /* 0 is an invalid runtime address */ }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai51100.00%1100.00%
Total51100.00%1100.00%


static int of_rsb_register_devices(struct sunxi_rsb *rsb) { struct device *dev = rsb->dev; struct device_node *child, *np = dev->of_node; u32 hwaddr; u8 rtaddr; int ret; if (!np) return -EINVAL; /* Runtime addresses for all slaves should be set first */ for_each_available_child_of_node(np, child) { dev_dbg(dev, "setting child %s runtime address\n", child->full_name); ret = of_property_read_u32(child, "reg", &hwaddr); if (ret) { dev_err(dev, "%s: invalid 'reg' property: %d\n", child->full_name, ret); continue; } rtaddr = sunxi_rsb_get_rtaddr(hwaddr); if (!rtaddr) { dev_err(dev, "%s: unknown hardware device address\n", child->full_name); continue; } /* * Since no devices have been registered yet, we are the * only ones using the bus, we can skip locking the bus. */ /* setup command parameters */ writel(RSB_CMD_STRA, rsb->regs + RSB_CMD); writel(RSB_DAR_RTA(rtaddr) | RSB_DAR_DA(hwaddr), rsb->regs + RSB_DAR); /* send command */ ret = _sunxi_rsb_run_xfer(rsb); if (ret) dev_warn(dev, "%s: set runtime address failed: %d\n", child->full_name, ret); } /* Then we start adding devices and probing them */ for_each_available_child_of_node(np, child) { struct sunxi_rsb_device *rdev; dev_dbg(dev, "adding child %s\n", child->full_name); ret = of_property_read_u32(child, "reg", &hwaddr); if (ret) continue; rtaddr = sunxi_rsb_get_rtaddr(hwaddr); if (!rtaddr) continue; rdev = sunxi_rsb_device_create(rsb, child, hwaddr, rtaddr); if (IS_ERR(rdev)) dev_err(dev, "failed to add child device %s: %ld\n", child->full_name, PTR_ERR(rdev)); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai279100.00%1100.00%
Total279100.00%1100.00%

static const struct of_device_id sunxi_rsb_of_match_table[] = { { .compatible = "allwinner,sun8i-a23-rsb" }, {} }; MODULE_DEVICE_TABLE(of, sunxi_rsb_of_match_table);
static int sunxi_rsb_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct resource *r; struct sunxi_rsb *rsb; unsigned long p_clk_freq; u32 clk_delay, clk_freq = 3000000; int clk_div, irq, ret; u32 reg; of_property_read_u32(np, "clock-frequency", &clk_freq); if (clk_freq > RSB_MAX_FREQ) { dev_err(dev, "clock-frequency (%u Hz) is too high (max = 20MHz)\n", clk_freq); return -EINVAL; } rsb = devm_kzalloc(dev, sizeof(*rsb), GFP_KERNEL); if (!rsb) return -ENOMEM; rsb->dev = dev; platform_set_drvdata(pdev, rsb); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); rsb->regs = devm_ioremap_resource(dev, r); if (IS_ERR(rsb->regs)) return PTR_ERR(rsb->regs); irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "failed to retrieve irq: %d\n", irq); return irq; } rsb->clk = devm_clk_get(dev, NULL); if (IS_ERR(rsb->clk)) { ret = PTR_ERR(rsb->clk); dev_err(dev, "failed to retrieve clk: %d\n", ret); return ret; } ret = clk_prepare_enable(rsb->clk); if (ret) { dev_err(dev, "failed to enable clk: %d\n", ret); return ret; } p_clk_freq = clk_get_rate(rsb->clk); rsb->rstc = devm_reset_control_get(dev, NULL); if (IS_ERR(rsb->rstc)) { ret = PTR_ERR(rsb->rstc); dev_err(dev, "failed to retrieve reset controller: %d\n", ret); goto err_clk_disable; } ret = reset_control_deassert(rsb->rstc); if (ret) { dev_err(dev, "failed to deassert reset line: %d\n", ret); goto err_clk_disable; } init_completion(&rsb->complete); mutex_init(&rsb->lock); /* reset the controller */ writel(RSB_CTRL_SOFT_RST, rsb->regs + RSB_CTRL); readl_poll_timeout(rsb->regs + RSB_CTRL, reg, !(reg & RSB_CTRL_SOFT_RST), 1000, 100000); /* * Clock frequency and delay calculation code is from * Allwinner U-boot sources. * * From A83 user manual: * bus clock frequency = parent clock frequency / (2 * (divider + 1)) */ clk_div = p_clk_freq / clk_freq / 2; if (!clk_div) clk_div = 1; else if (clk_div > RSB_CCR_MAX_CLK_DIV + 1) clk_div = RSB_CCR_MAX_CLK_DIV + 1; clk_delay = clk_div >> 1; if (!clk_delay) clk_delay = 1; dev_info(dev, "RSB running at %lu Hz\n", p_clk_freq / clk_div / 2); writel(RSB_CCR_SDA_OUT_DELAY(clk_delay) | RSB_CCR_CLK_DIV(clk_div - 1), rsb->regs + RSB_CCR); ret = devm_request_irq(dev, irq, sunxi_rsb_irq, 0, RSB_CTRL_NAME, rsb); if (ret) { dev_err(dev, "can't register interrupt handler irq %d: %d\n", irq, ret); goto err_reset_assert; } /* initialize all devices on the bus into RSB mode */ ret = sunxi_rsb_init_device_mode(rsb); if (ret) dev_warn(dev, "Initialize device mode failed: %d\n", ret); of_rsb_register_devices(rsb); return 0; err_reset_assert: reset_control_assert(rsb->rstc); err_clk_disable: clk_disable_unprepare(rsb->clk); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai565100.00%1100.00%
Total565100.00%1100.00%


static int sunxi_rsb_remove(struct platform_device *pdev) { struct sunxi_rsb *rsb = platform_get_drvdata(pdev); device_for_each_child(rsb->dev, NULL, sunxi_rsb_remove_devices); reset_control_assert(rsb->rstc); clk_disable_unprepare(rsb->clk); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai49100.00%1100.00%
Total49100.00%1100.00%

static struct platform_driver sunxi_rsb_driver = { .probe = sunxi_rsb_probe, .remove = sunxi_rsb_remove, .driver = { .name = RSB_CTRL_NAME, .of_match_table = sunxi_rsb_of_match_table, }, };
static int __init sunxi_rsb_init(void) { int ret; ret = bus_register(&sunxi_rsb_bus); if (ret) { pr_err("failed to register sunxi sunxi_rsb bus: %d\n", ret); return ret; } return platform_driver_register(&sunxi_rsb_driver); }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai43100.00%1100.00%
Total43100.00%1100.00%

module_init(sunxi_rsb_init);
static void __exit sunxi_rsb_exit(void) { platform_driver_unregister(&sunxi_rsb_driver); bus_unregister(&sunxi_rsb_bus); }

Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai21100.00%1100.00%
Total21100.00%1100.00%

module_exit(sunxi_rsb_exit); MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); MODULE_DESCRIPTION("Allwinner sunXi Reduced Serial Bus controller driver"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
chen-yu tsaichen-yu tsai304599.84%360.00%
dan carpenterdan carpenter30.10%120.00%
andre przywaraandre przywara20.07%120.00%
Total3050100.00%5100.00%
Directory: drivers/bus
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.