/*1* Bus driver for MIPS Common Device Memory Map (CDMM).2*3* Copyright (C) 2014-2015 Imagination Technologies Ltd.4*5* This file is subject to the terms and conditions of the GNU General Public6* License. See the file "COPYING" in the main directory of this archive7* for more details.8*/910#include <linux/atomic.h>11#include <linux/err.h>12#include <linux/cpu.h>13#include <linux/cpumask.h>14#include <linux/io.h>15#include <linux/of_address.h>16#include <linux/of.h>17#include <linux/platform_device.h>18#include <linux/slab.h>19#include <linux/smp.h>20#include <asm/cdmm.h>21#include <asm/hazards.h>22#include <asm/mipsregs.h>2324/* Access control and status register fields */25#define CDMM_ACSR_DEVTYPE_SHIFT 2426#define CDMM_ACSR_DEVTYPE (255ul << CDMM_ACSR_DEVTYPE_SHIFT)27#define CDMM_ACSR_DEVSIZE_SHIFT 1628#define CDMM_ACSR_DEVSIZE (31ul << CDMM_ACSR_DEVSIZE_SHIFT)29#define CDMM_ACSR_DEVREV_SHIFT 1230#define CDMM_ACSR_DEVREV (15ul << CDMM_ACSR_DEVREV_SHIFT)31#define CDMM_ACSR_UW (1ul << 3)32#define CDMM_ACSR_UR (1ul << 2)33#define CDMM_ACSR_SW (1ul << 1)34#define CDMM_ACSR_SR (1ul << 0)3536/* Each block of device registers is 64 bytes */37#define CDMM_DRB_SIZE 643839#define to_mips_cdmm_driver(d) container_of_const(d, struct mips_cdmm_driver, drv)4041/* Default physical base address */42static phys_addr_t mips_cdmm_default_base;4344/* Bus operations */4546static const struct mips_cdmm_device_id *47mips_cdmm_lookup(const struct mips_cdmm_device_id *table,48struct mips_cdmm_device *dev)49{50int ret = 0;5152for (; table->type; ++table) {53ret = (dev->type == table->type);54if (ret)55break;56}5758return ret ? table : NULL;59}6061static int mips_cdmm_match(struct device *dev, const struct device_driver *drv)62{63struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);64const struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(drv);6566return mips_cdmm_lookup(cdrv->id_table, cdev) != NULL;67}6869static int mips_cdmm_uevent(const struct device *dev, struct kobj_uevent_env *env)70{71const struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);72int retval = 0;7374retval = add_uevent_var(env, "CDMM_CPU=%u", cdev->cpu);75if (retval)76return retval;7778retval = add_uevent_var(env, "CDMM_TYPE=0x%02x", cdev->type);79if (retval)80return retval;8182retval = add_uevent_var(env, "CDMM_REV=%u", cdev->rev);83if (retval)84return retval;8586retval = add_uevent_var(env, "MODALIAS=mipscdmm:t%02X", cdev->type);87return retval;88}8990/* Device attributes */9192#define CDMM_ATTR(name, fmt, arg...) \93static ssize_t name##_show(struct device *_dev, \94struct device_attribute *attr, char *buf) \95{ \96struct mips_cdmm_device *dev = to_mips_cdmm_device(_dev); \97return sprintf(buf, fmt, arg); \98} \99static DEVICE_ATTR_RO(name);100101CDMM_ATTR(cpu, "%u\n", dev->cpu);102CDMM_ATTR(type, "0x%02x\n", dev->type);103CDMM_ATTR(revision, "%u\n", dev->rev);104CDMM_ATTR(modalias, "mipscdmm:t%02X\n", dev->type);105CDMM_ATTR(resource, "\t%016llx\t%016llx\t%016lx\n",106(unsigned long long)dev->res.start,107(unsigned long long)dev->res.end,108dev->res.flags);109110static struct attribute *mips_cdmm_dev_attrs[] = {111&dev_attr_cpu.attr,112&dev_attr_type.attr,113&dev_attr_revision.attr,114&dev_attr_modalias.attr,115&dev_attr_resource.attr,116NULL,117};118ATTRIBUTE_GROUPS(mips_cdmm_dev);119120const struct bus_type mips_cdmm_bustype = {121.name = "cdmm",122.dev_groups = mips_cdmm_dev_groups,123.match = mips_cdmm_match,124.uevent = mips_cdmm_uevent,125};126EXPORT_SYMBOL_GPL(mips_cdmm_bustype);127128/*129* Standard driver callback helpers.130*131* All the CDMM driver callbacks need to be executed on the appropriate CPU from132* workqueues. For the standard driver callbacks we need a work function133* (mips_cdmm_{void,int}_work()) to do the actual call from the right CPU, and a134* wrapper function (generated with BUILD_PERCPU_HELPER) to arrange for the work135* function to be called on that CPU.136*/137138/**139* struct mips_cdmm_work_dev - Data for per-device call work.140* @fn: CDMM driver callback function to call for the device.141* @dev: CDMM device to pass to @fn.142*/143struct mips_cdmm_work_dev {144void *fn;145struct mips_cdmm_device *dev;146};147148/**149* mips_cdmm_void_work() - Call a void returning CDMM driver callback.150* @data: struct mips_cdmm_work_dev pointer.151*152* A work_on_cpu() callback function to call an arbitrary CDMM driver callback153* function which doesn't return a value.154*/155static long mips_cdmm_void_work(void *data)156{157struct mips_cdmm_work_dev *work = data;158void (*fn)(struct mips_cdmm_device *) = work->fn;159160fn(work->dev);161return 0;162}163164/**165* mips_cdmm_int_work() - Call an int returning CDMM driver callback.166* @data: struct mips_cdmm_work_dev pointer.167*168* A work_on_cpu() callback function to call an arbitrary CDMM driver callback169* function which returns an int.170*/171static long mips_cdmm_int_work(void *data)172{173struct mips_cdmm_work_dev *work = data;174int (*fn)(struct mips_cdmm_device *) = work->fn;175176return fn(work->dev);177}178179#define _BUILD_RET_void180#define _BUILD_RET_int return181182/**183* BUILD_PERCPU_HELPER() - Helper to call a CDMM driver callback on right CPU.184* @_ret: Return type (void or int).185* @_name: Name of CDMM driver callback function.186*187* Generates a specific device callback function to call a CDMM driver callback188* function on the appropriate CPU for the device, and if applicable return the189* result.190*/191#define BUILD_PERCPU_HELPER(_ret, _name) \192static _ret mips_cdmm_##_name(struct device *dev) \193{ \194struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev); \195struct mips_cdmm_driver *cdrv = to_mips_cdmm_driver(dev->driver); \196struct mips_cdmm_work_dev work = { \197.fn = cdrv->_name, \198.dev = cdev, \199}; \200\201_BUILD_RET_##_ret work_on_cpu(cdev->cpu, \202mips_cdmm_##_ret##_work, &work); \203}204205/* Driver callback functions */206BUILD_PERCPU_HELPER(int, probe) /* int mips_cdmm_probe(struct device) */207BUILD_PERCPU_HELPER(int, remove) /* int mips_cdmm_remove(struct device) */208BUILD_PERCPU_HELPER(void, shutdown) /* void mips_cdmm_shutdown(struct device) */209210211/* Driver registration */212213/**214* mips_cdmm_driver_register() - Register a CDMM driver.215* @drv: CDMM driver information.216*217* Register a CDMM driver with the CDMM subsystem. The driver will be informed218* of matching devices which are discovered.219*220* Returns: 0 on success.221*/222int mips_cdmm_driver_register(struct mips_cdmm_driver *drv)223{224drv->drv.bus = &mips_cdmm_bustype;225226if (drv->probe)227drv->drv.probe = mips_cdmm_probe;228if (drv->remove)229drv->drv.remove = mips_cdmm_remove;230if (drv->shutdown)231drv->drv.shutdown = mips_cdmm_shutdown;232233return driver_register(&drv->drv);234}235EXPORT_SYMBOL_GPL(mips_cdmm_driver_register);236237/**238* mips_cdmm_driver_unregister() - Unregister a CDMM driver.239* @drv: CDMM driver information.240*241* Unregister a CDMM driver from the CDMM subsystem.242*/243void mips_cdmm_driver_unregister(struct mips_cdmm_driver *drv)244{245driver_unregister(&drv->drv);246}247EXPORT_SYMBOL_GPL(mips_cdmm_driver_unregister);248249250/* CDMM initialisation and bus discovery */251252/**253* struct mips_cdmm_bus - Info about CDMM bus.254* @phys: Physical address at which it is mapped.255* @regs: Virtual address where registers can be accessed.256* @drbs: Total number of DRBs.257* @drbs_reserved: Number of DRBs reserved.258* @discovered: Whether the devices on the bus have been discovered yet.259* @offline: Whether the CDMM bus is going offline (or very early260* coming back online), in which case it should be261* reconfigured each time.262*/263struct mips_cdmm_bus {264phys_addr_t phys;265void __iomem *regs;266unsigned int drbs;267unsigned int drbs_reserved;268bool discovered;269bool offline;270};271272static struct mips_cdmm_bus mips_cdmm_boot_bus;273static DEFINE_PER_CPU(struct mips_cdmm_bus *, mips_cdmm_buses);274static atomic_t mips_cdmm_next_id = ATOMIC_INIT(-1);275276/**277* mips_cdmm_get_bus() - Get the per-CPU CDMM bus information.278*279* Get information about the per-CPU CDMM bus, if the bus is present.280*281* The caller must prevent migration to another CPU, either by disabling282* pre-emption or by running from a pinned kernel thread.283*284* Returns: Pointer to CDMM bus information for the current CPU.285* May return ERR_PTR(-errno) in case of error, so check with286* IS_ERR().287*/288static struct mips_cdmm_bus *mips_cdmm_get_bus(void)289{290struct mips_cdmm_bus *bus, **bus_p;291unsigned long flags;292unsigned int cpu;293294if (!cpu_has_cdmm)295return ERR_PTR(-ENODEV);296297cpu = smp_processor_id();298/* Avoid early use of per-cpu primitives before initialised */299if (cpu == 0)300return &mips_cdmm_boot_bus;301302/* Get bus pointer */303bus_p = per_cpu_ptr(&mips_cdmm_buses, cpu);304local_irq_save(flags);305bus = *bus_p;306/* Attempt allocation if NULL */307if (unlikely(!bus)) {308bus = kzalloc(sizeof(*bus), GFP_ATOMIC);309if (unlikely(!bus))310bus = ERR_PTR(-ENOMEM);311else312*bus_p = bus;313}314local_irq_restore(flags);315return bus;316}317318/**319* mips_cdmm_cur_base() - Find current physical base address of CDMM region.320*321* Returns: Physical base address of CDMM region according to cdmmbase CP0322* register, or 0 if the CDMM region is disabled.323*/324static phys_addr_t mips_cdmm_cur_base(void)325{326unsigned long cdmmbase = read_c0_cdmmbase();327328if (!(cdmmbase & MIPS_CDMMBASE_EN))329return 0;330331return (cdmmbase >> MIPS_CDMMBASE_ADDR_SHIFT)332<< MIPS_CDMMBASE_ADDR_START;333}334335/**336* mips_cdmm_phys_base() - Choose a physical base address for CDMM region.337*338* Picking a suitable physical address at which to map the CDMM region is339* platform specific, so this weak function can be overridden by platform340* code to pick a suitable value if none is configured by the bootloader.341* By default this method tries to find a CDMM-specific node in the system342* dtb. Note that this won't work for early serial console.343*/344phys_addr_t __weak mips_cdmm_phys_base(void)345{346struct device_node *np;347struct resource res;348int err;349350np = of_find_compatible_node(NULL, NULL, "mti,mips-cdmm");351if (np) {352err = of_address_to_resource(np, 0, &res);353of_node_put(np);354if (!err)355return res.start;356}357358return 0;359}360361/**362* mips_cdmm_setup() - Ensure the CDMM bus is initialised and usable.363* @bus: Pointer to bus information for current CPU.364* IS_ERR(bus) is checked, so no need for caller to check.365*366* The caller must prevent migration to another CPU, either by disabling367* pre-emption or by running from a pinned kernel thread.368*369* Returns 0 on success, -errno on failure.370*/371static int mips_cdmm_setup(struct mips_cdmm_bus *bus)372{373unsigned long cdmmbase, flags;374int ret = 0;375376if (IS_ERR(bus))377return PTR_ERR(bus);378379local_irq_save(flags);380/* Don't set up bus a second time unless marked offline */381if (bus->offline) {382/* If CDMM region is still set up, nothing to do */383if (bus->phys == mips_cdmm_cur_base())384goto out;385/*386* The CDMM region isn't set up as expected, so it needs387* reconfiguring, but then we can stop checking it.388*/389bus->offline = false;390} else if (bus->phys > 1) {391goto out;392}393394/* If the CDMM region is already configured, inherit that setup */395if (!bus->phys)396bus->phys = mips_cdmm_cur_base();397/* Otherwise, ask platform code for suggestions */398if (!bus->phys)399bus->phys = mips_cdmm_phys_base();400/* Otherwise, copy what other CPUs have done */401if (!bus->phys)402bus->phys = mips_cdmm_default_base;403/* Otherwise, complain once */404if (!bus->phys) {405bus->phys = 1;406/*407* If you hit this, either your bootloader needs to set up the408* CDMM on the boot CPU, or else you need to implement409* mips_cdmm_phys_base() for your platform (see asm/cdmm.h).410*/411pr_err("cdmm%u: Failed to choose a physical base\n",412smp_processor_id());413}414/* Already complained? */415if (bus->phys == 1) {416ret = -ENOMEM;417goto out;418}419/* Record our success for other CPUs to copy */420mips_cdmm_default_base = bus->phys;421422pr_debug("cdmm%u: Enabling CDMM region at %pa\n",423smp_processor_id(), &bus->phys);424425/* Enable CDMM */426cdmmbase = read_c0_cdmmbase();427cdmmbase &= (1ul << MIPS_CDMMBASE_ADDR_SHIFT) - 1;428cdmmbase |= (bus->phys >> MIPS_CDMMBASE_ADDR_START)429<< MIPS_CDMMBASE_ADDR_SHIFT;430cdmmbase |= MIPS_CDMMBASE_EN;431write_c0_cdmmbase(cdmmbase);432tlbw_use_hazard();433434bus->regs = (void __iomem *)CKSEG1ADDR(bus->phys);435bus->drbs = 1 + ((cdmmbase & MIPS_CDMMBASE_SIZE) >>436MIPS_CDMMBASE_SIZE_SHIFT);437bus->drbs_reserved = !!(cdmmbase & MIPS_CDMMBASE_CI);438439out:440local_irq_restore(flags);441return ret;442}443444/**445* mips_cdmm_early_probe() - Minimally probe for a specific device on CDMM.446* @dev_type: CDMM type code to look for.447*448* Minimally configure the in-CPU Common Device Memory Map (CDMM) and look for a449* specific device. This can be used to find a device very early in boot for450* example to configure an early FDC console device.451*452* The caller must prevent migration to another CPU, either by disabling453* pre-emption or by running from a pinned kernel thread.454*455* Returns: MMIO pointer to device memory. The caller can read the ACSR456* register to find more information about the device (such as the457* version number or the number of blocks).458* May return IOMEM_ERR_PTR(-errno) in case of error, so check with459* IS_ERR().460*/461void __iomem *mips_cdmm_early_probe(unsigned int dev_type)462{463struct mips_cdmm_bus *bus;464void __iomem *cdmm;465u32 acsr;466unsigned int drb, type, size;467int err;468469if (WARN_ON(!dev_type))470return IOMEM_ERR_PTR(-ENODEV);471472bus = mips_cdmm_get_bus();473err = mips_cdmm_setup(bus);474if (err)475return IOMEM_ERR_PTR(err);476477/* Skip the first block if it's reserved for more registers */478drb = bus->drbs_reserved;479cdmm = bus->regs;480481/* Look for a specific device type */482for (; drb < bus->drbs; drb += size + 1) {483acsr = __raw_readl(cdmm + drb * CDMM_DRB_SIZE);484type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT;485if (type == dev_type)486return cdmm + drb * CDMM_DRB_SIZE;487size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT;488}489490return IOMEM_ERR_PTR(-ENODEV);491}492EXPORT_SYMBOL_GPL(mips_cdmm_early_probe);493494/**495* mips_cdmm_release() - Release a removed CDMM device.496* @dev: Device object497*498* Clean up the struct mips_cdmm_device for an unused CDMM device. This is499* called automatically by the driver core when a device is removed.500*/501static void mips_cdmm_release(struct device *dev)502{503struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev);504505kfree(cdev);506}507508/**509* mips_cdmm_bus_discover() - Discover the devices on the CDMM bus.510* @bus: CDMM bus information, must already be set up.511*/512static void mips_cdmm_bus_discover(struct mips_cdmm_bus *bus)513{514void __iomem *cdmm;515u32 acsr;516unsigned int drb, type, size, rev;517struct mips_cdmm_device *dev;518unsigned int cpu = smp_processor_id();519int ret = 0;520int id = 0;521522/* Skip the first block if it's reserved for more registers */523drb = bus->drbs_reserved;524cdmm = bus->regs;525526/* Discover devices */527bus->discovered = true;528pr_info("cdmm%u discovery (%u blocks)\n", cpu, bus->drbs);529for (; drb < bus->drbs; drb += size + 1) {530acsr = __raw_readl(cdmm + drb * CDMM_DRB_SIZE);531type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT;532size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT;533rev = (acsr & CDMM_ACSR_DEVREV) >> CDMM_ACSR_DEVREV_SHIFT;534535if (!type)536continue;537538pr_info("cdmm%u-%u: @%u (%#x..%#x), type 0x%02x, rev %u\n",539cpu, id, drb, drb * CDMM_DRB_SIZE,540(drb + size + 1) * CDMM_DRB_SIZE - 1,541type, rev);542543dev = kzalloc(sizeof(*dev), GFP_KERNEL);544if (!dev)545break;546547dev->cpu = cpu;548dev->res.start = bus->phys + drb * CDMM_DRB_SIZE;549dev->res.end = bus->phys +550(drb + size + 1) * CDMM_DRB_SIZE - 1;551dev->res.flags = IORESOURCE_MEM;552dev->type = type;553dev->rev = rev;554dev->dev.parent = get_cpu_device(cpu);555dev->dev.bus = &mips_cdmm_bustype;556dev->dev.id = atomic_inc_return(&mips_cdmm_next_id);557dev->dev.release = mips_cdmm_release;558559dev_set_name(&dev->dev, "cdmm%u-%u", cpu, id);560++id;561ret = device_register(&dev->dev);562if (ret)563put_device(&dev->dev);564}565}566567568/*569* CPU hotplug and initialisation570*571* All the CDMM driver callbacks need to be executed on the appropriate CPU from572* workqueues. For the CPU callbacks, they need to be called for all devices on573* that CPU, so the work function calls bus_for_each_dev, using a helper574* (generated with BUILD_PERDEV_HELPER) to call the driver callback if the575* device's CPU matches.576*/577578/**579* BUILD_PERDEV_HELPER() - Helper to call a CDMM driver callback if CPU matches.580* @_name: Name of CDMM driver callback function.581*582* Generates a bus_for_each_dev callback function to call a specific CDMM driver583* callback function for the device if the device's CPU matches that pointed to584* by the data argument.585*586* This is used for informing drivers for all devices on a given CPU of some587* event (such as the CPU going online/offline).588*589* It is expected to already be called from the appropriate CPU.590*/591#define BUILD_PERDEV_HELPER(_name) \592static int mips_cdmm_##_name##_helper(struct device *dev, void *data) \593{ \594struct mips_cdmm_device *cdev = to_mips_cdmm_device(dev); \595struct mips_cdmm_driver *cdrv; \596unsigned int cpu = *(unsigned int *)data; \597\598if (cdev->cpu != cpu || !dev->driver) \599return 0; \600\601cdrv = to_mips_cdmm_driver(dev->driver); \602if (!cdrv->_name) \603return 0; \604return cdrv->_name(cdev); \605}606607/* bus_for_each_dev callback helper functions */608BUILD_PERDEV_HELPER(cpu_down) /* int mips_cdmm_cpu_down_helper(...) */609BUILD_PERDEV_HELPER(cpu_up) /* int mips_cdmm_cpu_up_helper(...) */610611/**612* mips_cdmm_cpu_down_prep() - Callback for CPUHP DOWN_PREP:613* Tear down the CDMM bus.614* @cpu: unsigned int CPU number.615*616* This function is executed on the hotplugged CPU and calls the CDMM617* driver cpu_down callback for all devices on that CPU.618*/619static int mips_cdmm_cpu_down_prep(unsigned int cpu)620{621struct mips_cdmm_bus *bus;622long ret;623624/* Inform all the devices on the bus */625ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, &cpu,626mips_cdmm_cpu_down_helper);627628/*629* While bus is offline, each use of it should reconfigure it just in630* case it is first use when coming back online again.631*/632bus = mips_cdmm_get_bus();633if (!IS_ERR(bus))634bus->offline = true;635636return ret;637}638639/**640* mips_cdmm_cpu_online() - Callback for CPUHP ONLINE: Bring up the CDMM bus.641* @cpu: unsigned int CPU number.642*643* This work_on_cpu callback function is executed on a given CPU to discover644* CDMM devices on that CPU, or to call the CDMM driver cpu_up callback for all645* devices already discovered on that CPU.646*647* It is used as work_on_cpu callback function during648* initialisation. When CPUs are brought online the function is649* invoked directly on the hotplugged CPU.650*/651static int mips_cdmm_cpu_online(unsigned int cpu)652{653struct mips_cdmm_bus *bus;654long ret;655656bus = mips_cdmm_get_bus();657ret = mips_cdmm_setup(bus);658if (ret)659return ret;660661/* Bus now set up, so we can drop the offline flag if still set */662bus->offline = false;663664if (!bus->discovered)665mips_cdmm_bus_discover(bus);666else667/* Inform all the devices on the bus */668ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, &cpu,669mips_cdmm_cpu_up_helper);670671return ret;672}673674/**675* mips_cdmm_init() - Initialise CDMM bus.676*677* Initialise CDMM bus, discover CDMM devices for online CPUs, and arrange for678* hotplug notifications so the CDMM drivers can be kept up to date.679*/680static int __init mips_cdmm_init(void)681{682int ret;683684/* Register the bus */685ret = bus_register(&mips_cdmm_bustype);686if (ret)687return ret;688689/* We want to be notified about new CPUs */690ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "bus/cdmm:online",691mips_cdmm_cpu_online, mips_cdmm_cpu_down_prep);692if (ret < 0)693pr_warn("cdmm: Failed to register CPU notifier\n");694695return ret;696}697subsys_initcall(mips_cdmm_init);698699700