linux-stable/drivers/staging/fieldbus/anybuss/arcx-anybus.c
Linus Torvalds 556eb8b791 Driver core changes for 6.4-rc1
Here is the large set of driver core changes for 6.4-rc1.
 
 Once again, a busy development cycle, with lots of changes happening in
 the driver core in the quest to be able to move "struct bus" and "struct
 class" into read-only memory, a task now complete with these changes.
 
 This will make the future rust interactions with the driver core more
 "provably correct" as well as providing more obvious lifetime rules for
 all busses and classes in the kernel.
 
 The changes required for this did touch many individual classes and
 busses as many callbacks were changed to take const * parameters
 instead.  All of these changes have been submitted to the various
 subsystem maintainers, giving them plenty of time to review, and most of
 them actually did so.
 
 Other than those changes, included in here are a small set of other
 things:
   - kobject logging improvements
   - cacheinfo improvements and updates
   - obligatory fw_devlink updates and fixes
   - documentation updates
   - device property cleanups and const * changes
   - firwmare loader dependency fixes.
 
 All of these have been in linux-next for a while with no reported
 problems.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZEp7Sw8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ykitQCfamUHpxGcKOAGuLXMotXNakTEsxgAoIquENm5
 LEGadNS38k5fs+73UaxV
 =7K4B
 -----END PGP SIGNATURE-----

Merge tag 'driver-core-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core

Pull driver core updates from Greg KH:
 "Here is the large set of driver core changes for 6.4-rc1.

  Once again, a busy development cycle, with lots of changes happening
  in the driver core in the quest to be able to move "struct bus" and
  "struct class" into read-only memory, a task now complete with these
  changes.

  This will make the future rust interactions with the driver core more
  "provably correct" as well as providing more obvious lifetime rules
  for all busses and classes in the kernel.

  The changes required for this did touch many individual classes and
  busses as many callbacks were changed to take const * parameters
  instead. All of these changes have been submitted to the various
  subsystem maintainers, giving them plenty of time to review, and most
  of them actually did so.

  Other than those changes, included in here are a small set of other
  things:

   - kobject logging improvements

   - cacheinfo improvements and updates

   - obligatory fw_devlink updates and fixes

   - documentation updates

   - device property cleanups and const * changes

   - firwmare loader dependency fixes.

  All of these have been in linux-next for a while with no reported
  problems"

* tag 'driver-core-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (120 commits)
  device property: make device_property functions take const device *
  driver core: update comments in device_rename()
  driver core: Don't require dynamic_debug for initcall_debug probe timing
  firmware_loader: rework crypto dependencies
  firmware_loader: Strip off \n from customized path
  zram: fix up permission for the hot_add sysfs file
  cacheinfo: Add use_arch[|_cache]_info field/function
  arch_topology: Remove early cacheinfo error message if -ENOENT
  cacheinfo: Check cache properties are present in DT
  cacheinfo: Check sib_leaf in cache_leaves_are_shared()
  cacheinfo: Allow early level detection when DT/ACPI info is missing/broken
  cacheinfo: Add arm64 early level initializer implementation
  cacheinfo: Add arch specific early level initializer
  tty: make tty_class a static const structure
  driver core: class: remove struct class_interface * from callbacks
  driver core: class: mark the struct class in struct class_interface constant
  driver core: class: make class_register() take a const *
  driver core: class: mark class_release() as taking a const *
  driver core: remove incorrect comment for device_create*
  MIPS: vpe-cmp: remove module owner pointer from struct class usage.
  ...
2023-04-27 11:53:57 -07:00

376 lines
9.4 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Arcx Anybus-S Controller driver
*
* Copyright (C) 2018 Arcx Inc
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/delay.h>
#include <linux/idr.h>
#include <linux/mutex.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regmap.h>
/* move to <linux/anybuss-controller.h> when taking this out of staging */
#include "anybuss-controller.h"
#define CPLD_STATUS1 0x80
#define CPLD_CONTROL 0x80
#define CPLD_CONTROL_CRST 0x40
#define CPLD_CONTROL_RST1 0x04
#define CPLD_CONTROL_RST2 0x80
#define CPLD_STATUS1_AB 0x02
#define CPLD_STATUS1_CAN_POWER 0x01
#define CPLD_DESIGN_LO 0x81
#define CPLD_DESIGN_HI 0x82
#define CPLD_CAP 0x83
#define CPLD_CAP_COMPAT 0x01
#define CPLD_CAP_SEP_RESETS 0x02
struct controller_priv {
struct device *class_dev;
bool common_reset;
struct gpio_desc *reset_gpiod;
void __iomem *cpld_base;
struct mutex ctrl_lock; /* protects CONTROL register */
u8 control_reg;
char version[3];
u16 design_no;
};
static void do_reset(struct controller_priv *cd, u8 rst_bit, bool reset)
{
mutex_lock(&cd->ctrl_lock);
/*
* CPLD_CONTROL is write-only, so cache its value in
* cd->control_reg
*/
if (reset)
cd->control_reg &= ~rst_bit;
else
cd->control_reg |= rst_bit;
writeb(cd->control_reg, cd->cpld_base + CPLD_CONTROL);
/*
* h/w work-around:
* the hardware is 'too fast', so a reset followed by an immediate
* not-reset will _not_ change the anybus reset line in any way,
* losing the reset. to prevent this from happening, introduce
* a minimum reset duration.
* Verified minimum safe duration required using a scope
* on 14-June-2018: 100 us.
*/
if (reset)
usleep_range(100, 200);
mutex_unlock(&cd->ctrl_lock);
}
static int anybuss_reset(struct controller_priv *cd,
unsigned long id, bool reset)
{
if (id >= 2)
return -EINVAL;
if (cd->common_reset)
do_reset(cd, CPLD_CONTROL_CRST, reset);
else
do_reset(cd, id ? CPLD_CONTROL_RST2 : CPLD_CONTROL_RST1, reset);
return 0;
}
static void export_reset_0(struct device *dev, bool assert)
{
struct controller_priv *cd = dev_get_drvdata(dev);
anybuss_reset(cd, 0, assert);
}
static void export_reset_1(struct device *dev, bool assert)
{
struct controller_priv *cd = dev_get_drvdata(dev);
anybuss_reset(cd, 1, assert);
}
/*
* parallel bus limitation:
*
* the anybus is 8-bit wide. we can't assume that the hardware will translate
* word accesses on the parallel bus to multiple byte-accesses on the anybus.
*
* the imx WEIM bus does not provide this type of translation.
*
* to be safe, we will limit parallel bus accesses to a single byte
* at a time for now.
*/
static const struct regmap_config arcx_regmap_cfg = {
.reg_bits = 16,
.val_bits = 8,
.max_register = 0x7ff,
.use_single_read = true,
.use_single_write = true,
/*
* single-byte parallel bus accesses are atomic, so don't
* require any synchronization.
*/
.disable_locking = true,
};
static struct regmap *create_parallel_regmap(struct platform_device *pdev,
int idx)
{
void __iomem *base;
struct device *dev = &pdev->dev;
base = devm_platform_ioremap_resource(pdev, idx + 1);
if (IS_ERR(base))
return ERR_CAST(base);
return devm_regmap_init_mmio(dev, base, &arcx_regmap_cfg);
}
static struct anybuss_host *
create_anybus_host(struct platform_device *pdev, int idx)
{
struct anybuss_ops ops = {};
switch (idx) {
case 0:
ops.reset = export_reset_0;
break;
case 1:
ops.reset = export_reset_1;
break;
default:
return ERR_PTR(-EINVAL);
}
ops.host_idx = idx;
ops.regmap = create_parallel_regmap(pdev, idx);
if (IS_ERR(ops.regmap))
return ERR_CAST(ops.regmap);
ops.irq = platform_get_irq(pdev, idx);
if (ops.irq <= 0)
return ERR_PTR(-EINVAL);
return devm_anybuss_host_common_probe(&pdev->dev, &ops);
}
static ssize_t version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct controller_priv *cd = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", cd->version);
}
static DEVICE_ATTR_RO(version);
static ssize_t design_number_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct controller_priv *cd = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", cd->design_no);
}
static DEVICE_ATTR_RO(design_number);
static struct attribute *controller_attributes[] = {
&dev_attr_version.attr,
&dev_attr_design_number.attr,
NULL,
};
static const struct attribute_group controller_attribute_group = {
.attrs = controller_attributes,
};
static const struct attribute_group *controller_attribute_groups[] = {
&controller_attribute_group,
NULL,
};
static void controller_device_release(struct device *dev)
{
kfree(dev);
}
static int can_power_is_enabled(struct regulator_dev *rdev)
{
struct controller_priv *cd = rdev_get_drvdata(rdev);
return !(readb(cd->cpld_base + CPLD_STATUS1) & CPLD_STATUS1_CAN_POWER);
}
static const struct regulator_ops can_power_ops = {
.is_enabled = can_power_is_enabled,
};
static const struct regulator_desc can_power_desc = {
.name = "regulator-can-power",
.id = -1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.ops = &can_power_ops,
};
static struct class *controller_class;
static DEFINE_IDA(controller_index_ida);
static int controller_probe(struct platform_device *pdev)
{
struct controller_priv *cd;
struct device *dev = &pdev->dev;
struct regulator_config config = { };
struct regulator_dev *regulator;
int err, id;
struct anybuss_host *host;
u8 status1, cap;
cd = devm_kzalloc(dev, sizeof(*cd), GFP_KERNEL);
if (!cd)
return -ENOMEM;
dev_set_drvdata(dev, cd);
mutex_init(&cd->ctrl_lock);
cd->reset_gpiod = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(cd->reset_gpiod))
return PTR_ERR(cd->reset_gpiod);
/* CPLD control memory, sits at index 0 */
cd->cpld_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(cd->cpld_base)) {
dev_err(dev,
"failed to map cpld base address\n");
err = PTR_ERR(cd->cpld_base);
goto out_reset;
}
/* identify cpld */
status1 = readb(cd->cpld_base + CPLD_STATUS1);
cd->design_no = (readb(cd->cpld_base + CPLD_DESIGN_HI) << 8) |
readb(cd->cpld_base + CPLD_DESIGN_LO);
snprintf(cd->version, sizeof(cd->version), "%c%d",
'A' + ((status1 >> 5) & 0x7),
(status1 >> 2) & 0x7);
dev_info(dev, "design number %d, revision %s\n",
cd->design_no,
cd->version);
cap = readb(cd->cpld_base + CPLD_CAP);
if (!(cap & CPLD_CAP_COMPAT)) {
dev_err(dev, "unsupported controller [cap=0x%02X]", cap);
err = -ENODEV;
goto out_reset;
}
if (status1 & CPLD_STATUS1_AB) {
dev_info(dev, "has anybus-S slot(s)");
cd->common_reset = !(cap & CPLD_CAP_SEP_RESETS);
dev_info(dev, "supports %s", cd->common_reset ?
"a common reset" : "separate resets");
for (id = 0; id < 2; id++) {
host = create_anybus_host(pdev, id);
if (!IS_ERR(host))
continue;
err = PTR_ERR(host);
/* -ENODEV is fine, it just means no card detected */
if (err != -ENODEV)
goto out_reset;
}
}
id = ida_simple_get(&controller_index_ida, 0, 0, GFP_KERNEL);
if (id < 0) {
err = id;
goto out_reset;
}
/* export can power readout as a regulator */
config.dev = dev;
config.driver_data = cd;
regulator = devm_regulator_register(dev, &can_power_desc, &config);
if (IS_ERR(regulator)) {
err = PTR_ERR(regulator);
goto out_ida;
}
/* make controller info visible to userspace */
cd->class_dev = kzalloc(sizeof(*cd->class_dev), GFP_KERNEL);
if (!cd->class_dev) {
err = -ENOMEM;
goto out_ida;
}
cd->class_dev->class = controller_class;
cd->class_dev->groups = controller_attribute_groups;
cd->class_dev->parent = dev;
cd->class_dev->id = id;
cd->class_dev->release = controller_device_release;
dev_set_name(cd->class_dev, "%d", cd->class_dev->id);
dev_set_drvdata(cd->class_dev, cd);
err = device_register(cd->class_dev);
if (err)
goto out_dev;
return 0;
out_dev:
put_device(cd->class_dev);
out_ida:
ida_simple_remove(&controller_index_ida, id);
out_reset:
gpiod_set_value_cansleep(cd->reset_gpiod, 1);
return err;
}
static void controller_remove(struct platform_device *pdev)
{
struct controller_priv *cd = platform_get_drvdata(pdev);
int id = cd->class_dev->id;
device_unregister(cd->class_dev);
ida_simple_remove(&controller_index_ida, id);
gpiod_set_value_cansleep(cd->reset_gpiod, 1);
}
static const struct of_device_id controller_of_match[] = {
{ .compatible = "arcx,anybus-controller" },
{ }
};
MODULE_DEVICE_TABLE(of, controller_of_match);
static struct platform_driver controller_driver = {
.probe = controller_probe,
.remove_new = controller_remove,
.driver = {
.name = "arcx-anybus-controller",
.of_match_table = of_match_ptr(controller_of_match),
},
};
static int __init controller_init(void)
{
int err;
controller_class = class_create("arcx_anybus_controller");
if (IS_ERR(controller_class))
return PTR_ERR(controller_class);
err = platform_driver_register(&controller_driver);
if (err)
class_destroy(controller_class);
return err;
}
static void __exit controller_exit(void)
{
platform_driver_unregister(&controller_driver);
class_destroy(controller_class);
ida_destroy(&controller_index_ida);
}
module_init(controller_init);
module_exit(controller_exit);
MODULE_DESCRIPTION("Arcx Anybus-S Controller driver");
MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
MODULE_LICENSE("GPL v2");